<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Seth Michael Larson</title>
    <description>The latest articles on DEV Community by Seth Michael Larson (@sethmlarson).</description>
    <link>https://dev.to/sethmlarson</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3372%2F18519037.jpeg</url>
      <title>DEV Community: Seth Michael Larson</title>
      <link>https://dev.to/sethmlarson</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/sethmlarson"/>
    <language>en</language>
    <item>
      <title>The problem with Flask async views and async globals</title>
      <dc:creator>Seth Michael Larson</dc:creator>
      <pubDate>Mon, 02 Aug 2021 18:11:41 +0000</pubDate>
      <link>https://dev.to/sethmlarson/the-problem-with-flask-async-views-and-async-globals-pl</link>
      <guid>https://dev.to/sethmlarson/the-problem-with-flask-async-views-and-async-globals-pl</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;This is a &lt;a href="https://sethmlarson.dev/blog/2021-08-01/flask-async-views-and-async-globals"&gt;cross-post&lt;/a&gt; from my blog. If you enjoy my content you can subscribe via &lt;a href="https://tinyletter.com/sethmlarson"&gt;Email&lt;/a&gt; or &lt;a href="https://sethmlarson.dev/feed"&gt;RSS&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Starting in v2.0 &lt;a href="https://flask.palletsprojects.com/en/2.0.x/async-await"&gt;Flask has added async views&lt;/a&gt; which allow using &lt;code&gt;async&lt;/code&gt; and &lt;code&gt;await&lt;/code&gt; within a view function. This allows you to use other async APIs when building a web application with Flask.&lt;/p&gt;

&lt;p&gt;If you're planning on using Flask's async views there's a consideration to be aware of for using globally defined API clients or fixtures that are async.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating an async Flask application
&lt;/h2&gt;

&lt;p&gt;In this example we're using the &lt;a href="https://elasticsearch-py.readthedocs.io/en/latest/async.html"&gt;async Elasticsearch Python client&lt;/a&gt; as our global fixture. We initialize a simple Flask application with a single async view that makes a request with the async Elasticsearch client:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# app.py
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;flask&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Flask&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;jsonify&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;elasticsearch&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;AsyncElasticsearch&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Flask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;__name__&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Create the AsyncElasticsearch instance in the global scope.
&lt;/span&gt;&lt;span class="n"&gt;es&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;AsyncElasticsearch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"https://localhost:9200"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;api_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"..."&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;methods&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"GET"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;async_view&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;jsonify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;es&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;()))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Running with &lt;code&gt;gunicorn&lt;/code&gt; via &lt;code&gt;$gunicorn app:app&lt;/code&gt; and then visiting the app via &lt;code&gt;http://localhost:8000&lt;/code&gt;. After the first request everything looks fine:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"cluster_name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"d31d9d6abb334a398210484d7ac8567b"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"cluster_uuid"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"K5uyniiMT9u2grNBmsSt_Q"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"instance-0000000001"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"tagline"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"You Know, for Search"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"build_date"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2021-04-20T20:56:39.040728659Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"build_flavor"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"default"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"build_hash"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"3186837139b9c6b6d23c3200870651f10d3343b7"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"build_snapshot"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"build_type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"docker"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"lucene_version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"8.8.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"minimum_index_compatibility_version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"6.0.0-beta1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"minimum_wire_compatibility_version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"6.8.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"number"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"7.13.1"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However when you refresh the page to send a second request you receive an &lt;code&gt;InternalError&lt;/code&gt; and the following traceback:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Traceback (most recent call last):
  ...
  File "/app/app.py", line 13, in async_route
    return jsonify(**(await es.info()))
  File "/app/venv/lib/...", line 288, in info
    return await self.transport.perform_request(
  File "/app/venv/lib/...", line 327, in perform_request
    raise e
  File "/app/venv/lib/...", line 296, in perform_request
    status, headers, data = await connection.perform_request(
  File "/app/venv/lib/...", line 312, in perform_request
    raise ConnectionError("N/A", str(e), e)

ConnectionError(Event loop is closed) caused by:
  RuntimeError(Event loop is closed)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Why is this happening?
&lt;/h2&gt;

&lt;p&gt;The error message mentions the event loop is closed, huh? To understand why this is happening you need to know how &lt;code&gt;AsyncElasticsearch&lt;/code&gt; is implemented and how async views in Flask work.&lt;/p&gt;

&lt;h3&gt;
  
  
  No global event loops
&lt;/h3&gt;

&lt;p&gt;Async code relies on something called an event loop. So any code using &lt;code&gt;async&lt;/code&gt; or &lt;code&gt;await&lt;/code&gt; can't execute without an event loop that is "running". The unfortunate thing is that there's no running event loop right when you start Python (ie, the global scope).&lt;/p&gt;

&lt;p&gt;This is why you can't have code that looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;f&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"I'm async!"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Can't do this!
&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;instead you have to use &lt;code&gt;asyncio.run()&lt;/code&gt; and typically an &lt;code&gt;async&lt;/code&gt; main/entrypoint function to use &lt;code&gt;await&lt;/code&gt; like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;asyncio&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;f&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"I'm async!"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c1"&gt;# asyncio starts an event loop here:
&lt;/span&gt;&lt;span class="n"&gt;asyncio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(There's an exception to this via &lt;code&gt;python -m asyncio&lt;/code&gt; / &lt;code&gt;IPython&lt;/code&gt;, but really this is running the REPL after starting an event loop)&lt;/p&gt;

&lt;p&gt;So if you need an event loop to run any async code, how can you define an &lt;code&gt;AsyncElasticsearch&lt;/code&gt; instance in the global scope?&lt;/p&gt;

&lt;h3&gt;
  
  
  How AsyncElasticsearch allows global definitions
&lt;/h3&gt;

&lt;p&gt;The magic of global definitions for &lt;code&gt;AsyncElasticsearch&lt;/code&gt; is delaying the full initialization of calling &lt;code&gt;asyncio.get_running_loop()&lt;/code&gt;, creating &lt;code&gt;aiohttp.Session&lt;/code&gt;, sniffing, etc until after we've received our first &lt;code&gt;async&lt;/code&gt; call. Once an &lt;code&gt;async&lt;/code&gt; call is made we can almost guarantee that there's a running event loop, because if there wasn't a running event loop the request wouldn't work out anyways.&lt;/p&gt;

&lt;p&gt;This is great for async programs especially, as typically a single event loop gets used throughout a single execution of the program and means you can create your &lt;code&gt;AsyncElasticsearch&lt;/code&gt; instance in the global scope how users create their synchronous &lt;code&gt;Elasticsearch&lt;/code&gt; client in the global scope.&lt;/p&gt;

&lt;p&gt;Using multiple event loops is tricky and would likely break many other libraries like &lt;code&gt;aiohttp&lt;/code&gt; in the process for no (?) benefit, so we don't support this configuration. Now how does this break when used with Flask's new async views?&lt;/p&gt;

&lt;h3&gt;
  
  
  New event loop per async request
&lt;/h3&gt;

&lt;p&gt;The simple explanation is that Flask uses WSGI to service HTTP requests and responses which doesn't support asynchronous I/O. Asynchronous code requires a running event loop to execute, so Flask needs to get a running event loop from somewhere in order to execute an async view.&lt;/p&gt;

&lt;p&gt;To do so, Flask will create a new event loop and start running the view within this new event loop for every execution of the async view. This means all the async and await calls within the view will see the same event loop, but any other request before or after this view will see a different event loop.&lt;/p&gt;

&lt;p&gt;The trouble comes when you want to use async fixtures that are in the global scope, which in my experience is common in small to medium Flask applications. Very unfortunate situation! So what can we do?&lt;/p&gt;

&lt;h2&gt;
  
  
  Fixing the problem
&lt;/h2&gt;

&lt;p&gt;The problem isn't with Flask or the Python Elasticsearch client, the problem is the incompatibility between WSGI&lt;br&gt;
and async globals. There are a couple of solutions, both of which involve &lt;a href="https://asgi.readthedocs.io"&gt;Async Server Gateway Interface (ASGI)&lt;/a&gt;, WSGI's async-flavored cousin which was designed with async programs in mind.&lt;/p&gt;
&lt;h3&gt;
  
  
  Use an ASGI framework and server
&lt;/h3&gt;

&lt;p&gt;One way to avoid the problem with WSGI completely is to simply use a native ASGI web application framework instead.&lt;br&gt;
There are a handful of popular and widely used ASGI frameworks you can choose from:&lt;/p&gt;

&lt;p&gt;If you're looking for an experience that's very similar to Flask you can use &lt;a href="https://pgjones.gitlab.io/quart"&gt;Quart&lt;/a&gt; which is inspired by Flask. Quart even has a &lt;a href="https://pgjones.gitlab.io/quart/how_to_guides/flask_migration.html"&gt;guide about how to migrate from a Flask application to using Quart&lt;/a&gt;! Flask's own documentation for async views actually &lt;a href="https://flask.palletsprojects.com/en/2.0.x/async-await/#when-to-use-quart-instead"&gt;recommends using Quart&lt;/a&gt; in some cases due to the performance hit from using a new event loop per request.&lt;/p&gt;

&lt;p&gt;If you're looking to learn something new you can check out &lt;a href="https://fastapi.tiangolo.com"&gt;FastAPI&lt;/a&gt; which includes a bunch of builtin functionality for documenting APIs, strict model declarations, and data validation.&lt;/p&gt;

&lt;p&gt;Something to keep in mind when developing an ASGI application is you need an &lt;a href="https://asgi.readthedocs.io/en/latest/implementations.html#servers"&gt;ASGI-compatible server&lt;/a&gt;. Common choices include &lt;a href="https://www.uvicorn.org"&gt;Uvicorn&lt;/a&gt;, &lt;a href="https://pgjones.gitlab.io/hypercorn/index.html"&gt;Hypercorn&lt;/a&gt;, and &lt;a href="http://github.com/django/daphne"&gt;Daphne&lt;/a&gt;. Another option is to use the &lt;a href="http://gunicorn.org"&gt;Gunicorn&lt;/a&gt; with &lt;a href="https://www.uvicorn.org/#running-with-gunicorn"&gt;Uvicorn workers&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;All the options mentioned above function pretty similarly so pick whichever one you like. My personal choice has historically been Gunicorn with Uvicorn workers because of how widely used and mature Gunicorn is relative to how new the other libraries are.&lt;/p&gt;

&lt;p&gt;You can do so like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ gunicorn app:app -k uvicorn.workers.UvicornWorker
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Use WsgiToAsgi from asgiref
&lt;/h3&gt;

&lt;p&gt;If you really love Flask and want to continue using it you can also use the &lt;a href="https://github.com/django/asgiref"&gt;asgiref&lt;/a&gt; package provides an easy wrapper called &lt;code&gt;WsgiToAsgi&lt;/code&gt; that converts a WSGI application to an ASGI application.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;flask&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Flask&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;jsonify&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;elasticsearch&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;AsyncElasticsearch&lt;/span&gt;

&lt;span class="c1"&gt;# Same definition as above...
&lt;/span&gt;&lt;span class="n"&gt;wsgi_app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Flask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;__name__&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;es&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;AsyncElasticsearch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"https://localhost:9200"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;api_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"..."&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;wsgi_app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;methods&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"GET"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;async_view&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;jsonify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;es&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;()))&lt;/span&gt;

&lt;span class="c1"&gt;# Convert the WSGI application to ASGI
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;asgiref.wsgi&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;WsgiToAsgi&lt;/span&gt;

&lt;span class="n"&gt;asgi_app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;WsgiToAsgi&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;wsgi_app&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example we're converting the WSGI application &lt;code&gt;wsgi_app&lt;/code&gt; into an ASGI application &lt;code&gt;asgi_app&lt;/code&gt; which means when we run the application a single event loop will be used for every request instead of a new event loop per request.&lt;/p&gt;

&lt;p&gt;This approach will still require you to use an ASGI-compatible server.&lt;/p&gt;

</description>
      <category>python</category>
      <category>opensource</category>
      <category>flask</category>
      <category>async</category>
    </item>
    <item>
      <title>Everything to know about Requests v2.26.0</title>
      <dc:creator>Seth Michael Larson</dc:creator>
      <pubDate>Tue, 13 Jul 2021 18:13:46 +0000</pubDate>
      <link>https://dev.to/sethmlarson/everything-to-know-about-requests-v2-26-0-jf5</link>
      <guid>https://dev.to/sethmlarson/everything-to-know-about-requests-v2-26-0-jf5</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;This is a &lt;a href="https://sethmlarson.dev/blog/2021-07-13/everything-to-know-about-requests-v2-26-0"&gt;cross-post&lt;/a&gt; from my blog. If you enjoy my content you can subscribe via &lt;a href="https://tinyletter.com/sethmlarson"&gt;Email&lt;/a&gt; or &lt;a href="https://sethmlarson.dev/feed"&gt;RSS&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://github.com/psf/requests/pull/5868"&gt;Requests v2.26.0&lt;/a&gt; is a large release which changes and removes many features and dependencies that you should know about when upgrading.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary of the release
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What changes are important?
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Changed the &lt;code&gt;requests[security]&lt;/code&gt; extra into a no-op. Can be safely removed for v2.24.0+ for almost all users (OpenSSL 1.1.1+ and not relying on specific features in pyOpenSSL)&lt;/li&gt;
&lt;li&gt;Dropped support for Python 3.5&lt;/li&gt;
&lt;li&gt;Changed encoding detection library for Python 3.6+ from &lt;code&gt;chardet&lt;/code&gt; to &lt;code&gt;charset_normalizer&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Added support for Brotli compressed response bodies&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  What should you do now?
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Upgrade to Requests v2.26.0 if you're not using Python 3.5 - Stop using &lt;code&gt;requests[security]&lt;/code&gt; and instead install just &lt;code&gt;requests&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Regenerate your lock files and pinned dependencies if you're using &lt;code&gt;pip-tools&lt;/code&gt;, &lt;code&gt;poetry&lt;/code&gt;, or &lt;code&gt;pipenv&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/psf/requests/pull/5868"&gt;Read the full set of changes&lt;/a&gt; for v2.26.0&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Encoding detection with charset_normalizer
&lt;/h2&gt;

&lt;p&gt;The following section has a brief discussion of licensing issues. Please remember that I am not a lawyer and don't claim to understand anything about open source licenses.&lt;/p&gt;

&lt;p&gt;Requests uses character detection of response bodies in order to reliably decode &lt;code&gt;bytes&lt;/code&gt; to &lt;code&gt;str&lt;/code&gt; when responses don't define what encoding to use via &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Type"&gt;&lt;code&gt;Content-Type&lt;/code&gt;&lt;/a&gt;. This feature only gets used when you call the &lt;code&gt;Response.text()&lt;/code&gt; API.&lt;/p&gt;

&lt;p&gt;The library that Requests uses for content encoding detection has for the past 10 years been &lt;a href="https://github.com/chardet/chardet"&gt;&lt;code&gt;chardet&lt;/code&gt;&lt;/a&gt; which is licensed LGPL-2.1.&lt;/p&gt;

&lt;p&gt;The LGPL-2.1 license is not a problem for almost all users, but an issue arises with statically linked Python applications which are pretty rare but becoming more common. When Requests is bundled with a static application users can no longer "switch out" &lt;code&gt;chardet&lt;/code&gt; for a different library which causes a problem with LGPL.&lt;/p&gt;

&lt;p&gt;Starting in v2.26.0 for Python 3 the new default library for encoding detection will be &lt;a href="https://github.com/Ousret/charset_normalizer"&gt;&lt;code&gt;charset_normalizer&lt;/code&gt;&lt;/a&gt; which is MIT licensed. The library itself is relatively young so a lot of work has gone into making sure users aren't broken with this change including extensive tests against real-life websites and comparing the results against &lt;code&gt;chardet&lt;/code&gt; to ensure better performance and accuracy in every case.&lt;/p&gt;

&lt;p&gt;Requests will continue to use &lt;code&gt;chardet&lt;/code&gt; if the library is installed in your environment. To take advantage of &lt;code&gt;charset_normalizer&lt;/code&gt; you &lt;strong&gt;must uninstall &lt;code&gt;chardet&lt;/code&gt; from your environment&lt;/strong&gt;. If you want to continue using &lt;code&gt;chardet&lt;/code&gt; with Requests on Python 3 you can install &lt;code&gt;chardet&lt;/code&gt; or install Requests using &lt;code&gt;requests[use_chardet_on_py3]&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ python -m pip install requests chardet

- OR -

$ python -m pip install requests[use_chardet_on_py3]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Removed the deprecated [security] extra
&lt;/h2&gt;

&lt;p&gt;Before &lt;a href="https://github.com/psf/requests/blob/master/HISTORY.md#2240-2020-06-17"&gt;Requests v2.24.0&lt;/a&gt; the pyOpenSSL implementation of TLS was used by default if it was available. This pyOpenSSL code is packaged along with urllib3 as a way to use Subject Name Identification (SNI) when Python was compiled against super-old OpenSSL versions that didn't support it yet.&lt;/p&gt;

&lt;p&gt;Thankfully these super-old versions of OpenSSL aren't common at all anymore! So now that pyOpenSSL code that urllib3 provides is a lot less useful and now a maintenance burden for our team, so we now have a long-term plan to eventually remove this code. The biggest dependency using this code was Requests, a logical first place to start the journey.&lt;/p&gt;

&lt;p&gt;Starting in &lt;a href="https://github.com/psf/requests/blob/master/HISTORY.md#2240-2020-06-17"&gt;Requests v2.24.0&lt;/a&gt; pyOpenSSL wouldn't be used unless Python wasn't compiled with TLS support (ie, no &lt;code&gt;ssl&lt;/code&gt; module) or if the OpenSSL version that Python was compiled against didn't support SNI. Basically the two rare scenarios where pyOpenSSL was actually useful!&lt;/p&gt;

&lt;p&gt;The release of v2.24.0 came and went quietly which signaled to our team that our long-term plan of actually removing pyOpenSSL will likely go smoothly. So in Requests v2.25.0 we officially deprecated the &lt;code&gt;requests[security]&lt;/code&gt; extra and in v2.26.0 the &lt;code&gt;[security]&lt;/code&gt; extra will be a no-op. Instead of installing &lt;code&gt;pyOpenSSL&lt;/code&gt; and &lt;code&gt;cryptography&lt;/code&gt; no dependencies will be installed.&lt;/p&gt;

&lt;p&gt;What this means for you is if you've got a list of dependencies that previously used &lt;code&gt;requests[security]&lt;/code&gt; you can remove the &lt;code&gt;[security]&lt;/code&gt; and only install &lt;code&gt;requests&lt;/code&gt;. If you have a lock file via a tool like &lt;code&gt;pip-tools&lt;/code&gt; or &lt;code&gt;poetry&lt;/code&gt; you can regenerate the lock file and potentially see &lt;code&gt;pyOpenSSL&lt;/code&gt; and &lt;code&gt;cryptography&lt;/code&gt; removed from your lock file. Woo!&lt;/p&gt;

&lt;h2&gt;
  
  
  Dropped support for Python 3.5
&lt;/h2&gt;

&lt;p&gt;Starting in &lt;a href="https://github.com/psf/requests/blob/master/HISTORY.md#2250-2020-11-11"&gt;Requests v2.25.0&lt;/a&gt; there was a notice for Python 3.5's deprecation in the changelog. Now that 2.26.0 has arrived Requests will only be supported with Python 2.7.x and 3.6+.&lt;/p&gt;

&lt;p&gt;This is a big win for Requests maintainers as it progressively becomes more and more difficult to maintain a codebase that supports a wide range of Python versions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Brotli support via urllib3
&lt;/h2&gt;

&lt;p&gt;Since v1.25 urllib3 has supported automatically decoding &lt;a href="https://datatracker.ietf.org/doc/html/rfc7932"&gt;Brotli-compressed&lt;/a&gt; HTTP response bodies using either &lt;a href="https://github.com/google/brotli"&gt;Google's &lt;code&gt;brotli&lt;/code&gt; library&lt;/a&gt; or the &lt;a href="https://github.com/python-hyper/brotlicffi"&gt;&lt;code&gt;brotlicffi&lt;/code&gt; library&lt;/a&gt; (previously named &lt;code&gt;brotlipy&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;Before v2.26.0 Requests would never emit an &lt;code&gt;Accept-Encoding&lt;/code&gt; header with &lt;code&gt;br&lt;/code&gt; signaling Brotli support even if &lt;code&gt;urllib3&lt;/code&gt; would have been able to decode the response. Now Requests will use urllib3's feature detection for Brotli and emit &lt;code&gt;Accept-Encoding: gzip, deflate, br&lt;/code&gt;. This is great news for servers that support Brotli on pre-compressed static resources like fonts, CSS, and JavaScript. Woo!&lt;/p&gt;

&lt;p&gt;To take advantage of Brotli decoding you need to install one of the Brotli libraries mentioned above. You can ensure you're getting the right library for your Python implementation by installing like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ python -m pip install urllib3[brotli]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>python</category>
      <category>opensource</category>
      <category>news</category>
    </item>
    <item>
      <title>API Design for Optional Async Context Managed Resources</title>
      <dc:creator>Seth Michael Larson</dc:creator>
      <pubDate>Tue, 11 Aug 2020 21:11:20 +0000</pubDate>
      <link>https://dev.to/sethmlarson/api-design-for-optional-async-context-managed-resources-4gm9</link>
      <guid>https://dev.to/sethmlarson/api-design-for-optional-async-context-managed-resources-4gm9</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;This is a &lt;a href="https://sethmlarson.dev/blog/2020-08-10/api-design-for-an-async-open"&gt;cross-post&lt;/a&gt; from my blog &lt;a href="https://sethmlarson.dev/blog"&gt;&lt;code&gt;Python ♥ HTTP&lt;/code&gt;&lt;/a&gt;. If you enjoy my content and want it sooner you can &lt;a href="https://sethmlarson.dev/blog/rss"&gt;follow me via RSS&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I had a &lt;a href="https://twitter.com/zzzeek/status/1292831225586028544"&gt;discussion on Twitter&lt;/a&gt; with Mike Bayer, author of SQLAlchemy, about API design regarding async functions that return a resource to optionally be used as a async context manager. I wanted to do a quick write-up so the results of the discussion wouldn't be lost to the Twitter aether. Here we go:&lt;/p&gt;

&lt;p&gt;Take the Python function &lt;a href="https://docs.python.org/3/library/functions.html#open"&gt;&lt;code&gt;open()&lt;/code&gt;&lt;/a&gt;. The function can be used in multiple ways:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Without a context manager,
# requires an explicit close():
&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"file.txt"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c1"&gt;# As a context manager, file is
# closed in __exit__():
&lt;/span&gt;&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nb"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"file.txt"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now what if we wanted the above but to be asynchronous? What if we wanted to write an async function (named &lt;code&gt;async_open()&lt;/code&gt;) which has the same behavior as &lt;code&gt;open()&lt;/code&gt; above but is all async?&lt;/p&gt;

&lt;p&gt;First let's start with the "without async context manager" case:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;async_open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"file.txt"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Seems straightforward enough. All the async magic would be within the function itself. Now what if we wanted to add the async context manager case?&lt;/p&gt;

&lt;p&gt;An API which returns an async context manager is typically a synchronous function itself so you don't have to write the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;async_open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"file.txt"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The above API in my opinion looks clunky to use. You may already see the problem between the two code samples above. We want &lt;code&gt;async_open&lt;/code&gt; to be both asynchronous and synchronous to fit both usages. Oh no!&lt;/p&gt;

&lt;p&gt;The big question is where do we put the async code that happens either when &lt;code&gt;await&lt;/code&gt; is used or when &lt;code&gt;async with&lt;/code&gt; is used without the user signalling which will be used.&lt;/p&gt;

&lt;p&gt;After a little bit of thinking I landed on the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AsyncFile&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="s"&gt;"""The object that has all the file object
    methods like read() and close() but async!
    """&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;read&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"read()"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"close()"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AwaitOrAenter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="s"&gt;"""The special object that handles either
    an __await__() call for the non-context
    managed case or an __aenter__() call for
    the context managed case.
    """&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;filepath&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_filepath&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;filepath&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;

    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_async_open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="c1"&gt;# ( Async magic and open an AsyncFile )
&lt;/span&gt;        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;AsyncFile&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_file&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__await__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_async_open&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="n"&gt;__await__&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__aenter__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_async_open&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__aexit__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;async_open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filepath&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;AwaitOrAenter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filepath&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The above API can now be used in both of these cases:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;async_open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"file.txt"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;async_open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"file.txt"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Looks like the API works as intended! 🥳&lt;/p&gt;

</description>
      <category>python</category>
      <category>async</category>
    </item>
    <item>
      <title>Why URLs are Hard: Path Parameters and urlparse</title>
      <dc:creator>Seth Michael Larson</dc:creator>
      <pubDate>Sat, 11 Apr 2020 19:18:48 +0000</pubDate>
      <link>https://dev.to/sethmlarson/why-urls-are-hard-path-parameters-and-urlparse-c2n</link>
      <guid>https://dev.to/sethmlarson/why-urls-are-hard-path-parameters-and-urlparse-c2n</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;This is a &lt;a href="https://sethmlarson.dev/blog/2020-04-10/why-urls-are-hard-path-params-urlparse"&gt;cross-post&lt;/a&gt; from my blog &lt;a href="https://sethmlarson.dev/blog"&gt;&lt;code&gt;Python ♥ HTTP&lt;/code&gt;&lt;/a&gt;. If you enjoy my content and want it sooner you can &lt;a href="https://sethmlarson.dev/blog/rss"&gt;follow me via RSS&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Welcome to the first installment of "Why URLs are Hard": a series of stories that I've accumulated from reading a lot about URLs.&lt;/p&gt;

&lt;p&gt;We take URLs for granted and mostly think of them as very simple things because of how often we interact with clean and simple URLs like &lt;code&gt;https://example.com&lt;/code&gt;. Little do you know there are decades of ancient dark magic that occurred before we ended up with URLs we know and love today.&lt;/p&gt;

&lt;p&gt;This story is about finding a mysterious API in Python's &lt;code&gt;urlparse&lt;/code&gt; function and discovering a now almost entirely unused URL feature. Come along with me! :)&lt;/p&gt;

&lt;h2&gt;
  
  
  Comparing urlparse to RFC 3986
&lt;/h2&gt;

&lt;p&gt;I was evaluating &lt;a href="https://docs.python.org/3/library/urllib.parse.html#urllib.parse.urlparse"&gt;&lt;code&gt;urlparse&lt;/code&gt;&lt;/a&gt; from the &lt;a href="https://docs.python.org/3/library/urllib.parse.html"&gt;&lt;code&gt;urllib.parse&lt;/code&gt;&lt;/a&gt; module and how it performed compared to other URL parser libraries.&lt;/p&gt;

&lt;p&gt;Within the documentation it's mentioned that URLs are parsed according to &lt;a href="https://tools.ietf.org/html/rfc3986.html"&gt;RFC 3986&lt;/a&gt; which is a set of rules that describe how to segment a URL into different components. Let's take a quick look at that standard to see what parts of a URL we see.&lt;/p&gt;

&lt;p&gt;There's a cute little ASCII diagram showing off all the parts of a URL:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; foo://example.com:8042/over/there?name=ferret#nose
 \_/   \______________/\_________/ \_________/ \__/
  |           |            |            |        |
scheme     authority       path        query   fragment
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;... and then the &lt;code&gt;authority&lt;/code&gt; section is further decomposed into:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;authority = [ userinfo "@" ] host [ ":" port ]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One of the best parts of reading RFCs is thinking about how much effort people put into the adorable ASCII art :)&lt;/p&gt;

&lt;p&gt;Okay, now that we know what to expect let's try out &lt;code&gt;urlparse&lt;/code&gt; with the URL from the RFC:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;urllib.parse&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;urlparse&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt; &lt;span class="s"&gt;"foo://user:pass@example.com:8042"&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt; &lt;span class="s"&gt;"/over/there?name=ferret#nose"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;parts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;urlparse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;parts&lt;/span&gt;
&lt;span class="n"&gt;ParseResult&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;scheme&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'foo'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;netloc&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'user:pass@example.com:8042'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'/over/there'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;''&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'name=ferret'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;fragment&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'nose'&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;parts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;hostname&lt;/span&gt;
&lt;span class="s"&gt;'example.com'&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;parts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;port&lt;/span&gt;
&lt;span class="mi"&gt;8042&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;parts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;username&lt;/span&gt;
&lt;span class="s"&gt;'user'&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;parts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;password&lt;/span&gt;
&lt;span class="s"&gt;'pass'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Okay so looks like we have this as a mapping from &lt;code&gt;ParseResult&lt;/code&gt; to RFC 3986:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;parts.scheme&lt;/code&gt; -&amp;gt; &lt;code&gt;scheme&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;parts.netloc&lt;/code&gt; -&amp;gt; &lt;code&gt;authority&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;parts.username&lt;/code&gt;:&lt;code&gt;password&lt;/code&gt; -&amp;gt; &lt;code&gt;userinfo&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;parts.hostname&lt;/code&gt; -&amp;gt; &lt;code&gt;host&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;parts.port&lt;/code&gt; -&amp;gt; &lt;code&gt;port&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;parts.path&lt;/code&gt; -&amp;gt; &lt;code&gt;path&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;parts.params&lt;/code&gt; -&amp;gt; ???&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;parts.query&lt;/code&gt; -&amp;gt; &lt;code&gt;query&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;parts.fragment&lt;/code&gt; -&amp;gt; &lt;code&gt;fragment&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Notice the ??? in the list? I was confused too. No matter what I put into my URL I couldn't get anything to show up in &lt;code&gt;ParseResult.params&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The documentation for &lt;code&gt;ParseResult.params&lt;/code&gt; is "Parameters for last path element" and then isn't mentioned much anywhere else. Googling around is tough too because "&lt;code&gt;params&lt;/code&gt;" is Requests way of adding to the query string for the requested URL so most results are about that.&lt;/p&gt;

&lt;p&gt;When googling "Path parameters" I found &lt;a href="https://doriantaylor.com/policy/http-url-path-parameter-syntax"&gt;this article from 2008&lt;/a&gt; which pointed to the last paragraph of &lt;a href="https://tools.ietf.org/html/rfc3986#section-3.3"&gt;RFC 3986 Section 3.3&lt;/a&gt; which explains path parameters:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Aside from dot-segments in hierarchical paths,
a path segment is considered opaque by the
generic syntax.  URI producing applications
often use the reserved characters allowed in a
segment to delimit scheme-specific or dereference-
handler-specific subcomponents.  For example,
the semicolon (";") and equals ("=") reserved
characters are often used to delimit parameters
and parameter values applicable to that segment.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So &lt;code&gt;;&lt;/code&gt; and &lt;code&gt;=&lt;/code&gt; have special meaning within the &lt;code&gt;path&lt;/code&gt;, let's throw those into &lt;code&gt;urlparse&lt;/code&gt; and see what happens:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;urlparse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"http://example.com/a;z=y;x/b;c;d=e"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;ParseResult&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;scheme&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'http'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;netloc&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'example.com'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'/a;z=y;x/b'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'c;d=e'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;''&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;fragment&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;''&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Huh, I didn't expect it to pull the values actually outside of the &lt;code&gt;path&lt;/code&gt; component. And it looks like it only pulled the params from the last segment, &lt;code&gt;/a;z=y;x/&lt;/code&gt; is untouched. Wonder how many bugs are lurking out there because of this quirk. :)&lt;/p&gt;

&lt;p&gt;So if you're relying on URL parsing and directly inspecting the &lt;code&gt;path&lt;/code&gt; component make sure you check your implementation and amend it to add &lt;code&gt;f";{result.params}"&lt;/code&gt; if &lt;code&gt;params&lt;/code&gt; is non-empty. Either that or use a URL parser that doesn't have this quirk like &lt;a href="https://github.com/python-hyper/rfc3986"&gt;&lt;code&gt;rfc3986&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I especially recommend using another library if you're making security decisions based on the URL. &lt;a href="https://www.jtmelton.com/2011/02/02/beware-the-http-path-parameter"&gt;A write-up from 2011 details a security issue related to path parameters&lt;/a&gt; which an application using &lt;code&gt;ParseResult.path&lt;/code&gt; alone would likely also be vulnerable to.&lt;/p&gt;

&lt;p&gt;Hope you learned something and stay safe!&lt;/p&gt;

</description>
      <category>python</category>
      <category>todayilearned</category>
      <category>url</category>
    </item>
    <item>
      <title>urllib3 in 2020</title>
      <dc:creator>Seth Michael Larson</dc:creator>
      <pubDate>Sat, 14 Mar 2020 17:40:21 +0000</pubDate>
      <link>https://dev.to/sethmlarson/urllib3-in-2020-2jik</link>
      <guid>https://dev.to/sethmlarson/urllib3-in-2020-2jik</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;This is a &lt;a href="https://sethmlarson.dev/blog/2020-03-14/urllib3-in-2020"&gt;cross-post&lt;/a&gt; from my blog &lt;a href="https://sethmlarson.dev/blog"&gt;&lt;code&gt;Python ♥ HTTP&lt;/code&gt;&lt;/a&gt;. If you enjoy my content and want it sooner you can &lt;a href="https://sethmlarson.dev/blog/rss"&gt;follow me via RSS&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://sethmlarson.dev/blog/2019-12-28/review-of-2019-for-urllib3"&gt;2019 was a transformative year for urllib3&lt;/a&gt; and I'm hoping to keep up the pace with Python's most used HTTP client. Here are a list of ideas, some easier achieved than others, that would be great to ship this year!&lt;/p&gt;

&lt;h2&gt;
  
  
  Acquire Funding for Larger Projects
&lt;/h2&gt;

&lt;p&gt;We're excited to be working with &lt;a href="https://tidelift.com/subscription/pkg/pypi-urllib3?utm_source=pypi-urllib3&amp;amp;utm_medium=referral&amp;amp;utm_campaign=blog"&gt;Tidelift&lt;/a&gt; for another year as one of the original lifted Python projects.&lt;br&gt;
They've enabled us to &lt;a href="https://blog.tidelift.com/why-coordinated-security-vulnerability-disclosure-policies-are-important"&gt;provide a comprehensive security disclosure workflow&lt;/a&gt; and help me and another maintainer to "keep the lights on" so to speak for the project.&lt;/p&gt;

&lt;p&gt;However for some of the larger goals mentioned in this post we'll likely require dedicated developer-hours to achieve. In 2019 ~78% of urllib3's $23,580 in funding came from grants. What urllib3 was able to achieve with the grants has increased the security of the project and benefited millions of people, your generous organization could help us do the same in 2020!&lt;/p&gt;

&lt;p&gt;If your organization benefits from urllib3 being secure, maintained, and performant, and maintained please consider &lt;a href="//mailto:sethmichaellarson@gmail.com"&gt;contacting me to discuss a grant opportunity&lt;/a&gt; or &lt;a href="https://tidelift.com/subscription/pkg/pypi-urllib3?utm_source=pypi-urllib3&amp;amp;utm_medium=referral&amp;amp;utm_campaign=blog"&gt;subscribing to Tidelift&lt;/a&gt; to keep urllib3 and many more Python projects secure and maintained.&lt;/p&gt;

&lt;h2&gt;
  
  
  Mentor New Contributors
&lt;/h2&gt;

&lt;p&gt;Python's HTTP stack has seen a surge of activity and will need even more helping hands to accomplish our goals and keep Python on the bleeding edge in the HTTP space and keep our dependable existing libraries up-to-date.&lt;/p&gt;

&lt;p&gt;If you're looking to get started in Python Open Source and have an interest in making high-impact contributions, learn a lot about networking, or bolster your resume &lt;a href="//mailto:sethmichaellarson@gmail.com"&gt;reach out to me&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use Operating System Trust Stores
&lt;/h2&gt;

&lt;p&gt;Python's HTTP clients all rely on a trust store named &lt;a href="https://github.com/certifi/python-certifi/"&gt;&lt;code&gt;certifi&lt;/code&gt;&lt;/a&gt; which is the &lt;a href="https://www.mozilla.org/en-US/about/governance/policies/security-group/certs/"&gt;Mozilla CA Certificate Store&lt;/a&gt;, packaged and released on PyPI. This works well for the Internet at large as it's the same CA certificate store that powers Firefox and curl. However many organizations and IT machine configurations only ensure that the operating system CA bundle is up to date and having &lt;code&gt;certifi&lt;/code&gt; be on separate means organizations can't control the safety of their users the same way that they control safety of browsers and other tools.&lt;/p&gt;

&lt;p&gt;We'd also want this work to be usable by other projects in the Python ecosystem as this is an important feature for any library that uses TLS.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/wbond/oscrypto/blob/master/oscrypto/trust_list.py"&gt;&lt;code&gt;oscrypto.trust_list&lt;/code&gt;&lt;/a&gt; in particular looks like a promising start.&lt;/p&gt;

&lt;h2&gt;
  
  
  TLS 1.2+ by Default
&lt;/h2&gt;

&lt;p&gt;Thanks to the security trail-blazing of many organizations TLS 1.2 is becoming the new minimum security level for the Internet. CDNs and cloud services mean almost all services deployed there use TLS 1.2+ whether their users know it or not by taking configuration of TLS termination away from users.&lt;/p&gt;

&lt;p&gt;Browsers are also doing their part by deprecating and eventually removing TLS 1.0 and 1.1 from future versions. Firefox, Edge, Safari, and Chrome have all committed to removing TLS &amp;lt;1.2 in 2020.&lt;/p&gt;

&lt;p&gt;This means 2020 is a great time for us to also push the better-security-by-force envelope. :)&lt;/p&gt;

&lt;p&gt;Setting TLS 1.2+ means that we'll be secure against TLS downgrade attacks or if a vulnerability is found in TLS 1.0 or 1.1 our users will already be protected. It also means that we can restrict our default cipher suites to rock-solid secure ones instead of ones that are there only to be compatible with older TLS versions.&lt;/p&gt;

&lt;p&gt;Even with all you'll still be able to use TLS 1.0 or 1.1 if you specify those versions manually via &lt;code&gt;ssl_version&lt;/code&gt; in the case you have to connect to an older server.&lt;/p&gt;

&lt;h2&gt;
  
  
  Drop Support for Python &amp;lt;2.7.9
&lt;/h2&gt;

&lt;p&gt;Python 2.7.9 is the first Python version that supported &lt;code&gt;ssl.SSLContext()&lt;/code&gt; objects. Currently urllib3 supports all Python 2.7 versions but maintains a large chunk of code that allows us to work with Python versions that don't have proper &lt;code&gt;SSLContext&lt;/code&gt; objects. This means that we're forced to do hostname verification ourselves rather than rely on OpenSSL's own implementation of certificate verification.&lt;/p&gt;

&lt;p&gt;We'll continue to support Python interpreters that don't have a compiled &lt;code&gt;ssl&lt;/code&gt; module, however HTTPS connections won't be possible as is the case today.&lt;/p&gt;

&lt;p&gt;We will also continue to in general support Python 2.7 for the forseeable future and won't consider removing support via a breaking change until Pip and other upstream projects also drop support.&lt;/p&gt;

&lt;p&gt;For some data on what percentage of downloads would be effected &lt;a href="https://gist.github.com/sethmlarson/8f2272c4c1b7f6f926bec6fbce6c5689"&gt;I've created a small data table&lt;/a&gt;. The number of downloads effected by the change would be ~4%.&lt;/p&gt;

&lt;h2&gt;
  
  
  Add Support for Zstandard
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://facebook.github.io/zstd/"&gt;Zstandard&lt;/a&gt; defined in &lt;a href="https://tools.ietf.org/html/rfc8478"&gt;RFC 8478&lt;/a&gt; is looking to be a great replacement for the long-dominant &lt;code&gt;gzip&lt;/code&gt; as a content encoding. With both great compression ratios and quick parallelizable CPU usage I'm guessing this technology will soon take the Internet by storm, especially at the edge / CDN layer.&lt;/p&gt;

&lt;p&gt;When Zstandard is standardized and accepted it'd be great to already have the feature in place to immediately start giving our users the new performance benefits.&lt;/p&gt;

&lt;h2&gt;
  
  
  Switch from Travis + AppVeyor to GitHub Actions
&lt;/h2&gt;

&lt;p&gt;The number of Python versions and configurations that urllib3 supports is quite large, testing against Linux, macOS, and Windows to ensure we're compatible with all major OS flavors. With Travis &lt;a href="https://twitter.com/hugovk/status/1233787684587556864"&gt;dropping the default concurrency level to 3 for OSS projects&lt;/a&gt; and AppVeyor continuing to support only 1 concurrent job we're feeling cornered into long CI durations.&lt;/p&gt;

&lt;p&gt;Fortunately we've been experiencing success with GitHub Actions as a CI platform in other projects with the 20 concurrent jobs as well as great flexibility and access to all platforms from one service and would like to switch wholesale to GA from Travis and AppVeyor.&lt;/p&gt;

</description>
      <category>python</category>
      <category>http</category>
    </item>
    <item>
      <title>urllib3 Sustainability and Achievements in 2019</title>
      <dc:creator>Seth Michael Larson</dc:creator>
      <pubDate>Sat, 28 Dec 2019 16:29:10 +0000</pubDate>
      <link>https://dev.to/sethmlarson/urllib3-sustainability-and-achievements-in-2019-4dfe</link>
      <guid>https://dev.to/sethmlarson/urllib3-sustainability-and-achievements-in-2019-4dfe</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;This is a &lt;a href="https://sethmlarson.dev/blog/2019-12-28/review-of-2019-for-urllib3"&gt;cross-post&lt;/a&gt; from my blog &lt;a href="https://sethmlarson.dev/blog"&gt;&lt;code&gt;Python ♥ HTTP&lt;/code&gt;&lt;/a&gt;. If you enjoy my content and want it sooner you can &lt;a href="https://sethmlarson.dev/blog/rss"&gt;follow me via RSS&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://github.com/urllib3/urllib3"&gt;urllib3&lt;/a&gt; has had probably one of it's most eventful years in recent times,&lt;br&gt;
especially with regards to sustainability of the project thanks to sponsors and grants.&lt;/p&gt;

&lt;p&gt;I'm looking forward to 2020 and have many ideas for where the project is headed that I'll&lt;br&gt;
be sharing in a future post. For now let's review what was accomplished in 2019:&lt;/p&gt;

&lt;h2&gt;
  
  
  Grants and Sponsorships
&lt;/h2&gt;

&lt;p&gt;urllib3 received &lt;strong&gt;$23,580&lt;/strong&gt; USD throughout the year of 2019.&lt;br&gt;
We're very grateful for our donators and sponsors, this year&lt;br&gt;
would not have been as productive without you. Thank you!&lt;/p&gt;

&lt;p&gt;Here's the breakdown on where that money came from:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Grant from &lt;a href="https://www.govcert.lu"&gt;CERT Governmental Luxembourg&lt;/a&gt; for &lt;strong&gt;$10,944&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Grant from &lt;a href="https://gitcoin.co/grants/65/urllib3"&gt;GitCoin Grants&lt;/a&gt; for &lt;strong&gt;$7,636&lt;/strong&gt; (1 DAI ~ $1)&lt;/li&gt;
&lt;li&gt;Sponsorship from &lt;a href="https://tidelift.com/lifter/search/pypi/urllib3"&gt;Tidelift&lt;/a&gt; for &lt;strong&gt;$5,000&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The breakdown above shows that most of our funding for this year came from grants.&lt;br&gt;
Hopefully we can continue this into 2020 as the major accomplishments for the project&lt;br&gt;
were completed as a result of dedicated developer(s) spending extended periods of time&lt;br&gt;
working on features.&lt;/p&gt;

&lt;p&gt;If you or your organization rely on urllib3 and would like to sponsor urllib3's development&lt;br&gt;
send an email to &lt;code&gt;sethmichaellarson@gmail.com&lt;/code&gt; and &lt;code&gt;andrey.petrov@shazow.net&lt;/code&gt;. &lt;/p&gt;

&lt;h2&gt;
  
  
  Releases and Changes
&lt;/h2&gt;

&lt;p&gt;urllib3 made 10 releases during 2019, up from only 3 releases during 2018.&lt;br&gt;
The highlights of those releases include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Strict compliance to RFC 3986 for URL parsing.&lt;br&gt;
This functionality was implemented as a part of the two grants&lt;br&gt;
listed above and helped protect users from the new class of&lt;br&gt;
attacks related to URL parsers. See CVE-2019-9740, CVE-2019-9636, CVE-2019-10160.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Added support for TLSv1.3 for OpenSSL 1.1.1+. This functionality was implemented&lt;br&gt;
as a part of the grant from GOVCERT LU. TLS 1.3 adds additional security and&lt;br&gt;
performance benefits for HTTPS connections.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Added automatic downstream integration testing for Requests and Botocore&lt;br&gt;
and automated deploys to PyPI from CI. This means we can ship releases more frequently&lt;br&gt;
and also be more confident that the changes being made won't break the universe.&lt;br&gt;
Our CI was also augmented to be less flaky resulting in smoother merges for Pull Requests.&lt;br&gt;
This work was done as a part of both above grants.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Added support for Brotli as a &lt;code&gt;Content-Encoding&lt;/code&gt;. This means that if the requested website&lt;br&gt;
also supports Brotli your response bodies will be even smaller than gzip and save bandwidth.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Added support for Python 3.8. Python 3.9 alphas have just started coming out and there are&lt;br&gt;
already issues on the horizon.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Achievements
&lt;/h2&gt;

&lt;p&gt;These achievements aren't related to library features but are still super-fun to celebrate!&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://twitter.com/sethmlarson/status/1182710786436882435"&gt;We eclipsed 1 billion (1,000,000,000) total downloads on PyPI&lt;/a&gt;,&lt;br&gt;
something that only ~10 projects have done. This number is unimaginably large and shows&lt;br&gt;
how essential a secure HTTP client library is to the Python ecosystem.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://twitter.com/llanga/status/1204820198894772224"&gt;We receive a majority of our downloads from Python 3.X instead of Python 2.X&lt;/a&gt;&lt;br&gt;
for the first time. About ~50% of all downloads still come from Python 2.7 but that number is very slowly decreasing&lt;br&gt;
over time.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/urllib3/urllib3"&gt;We have a logo now&lt;/a&gt; thanks to Ryan Feeley and Jess Shapiro! ♥&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Thank You
&lt;/h2&gt;

&lt;p&gt;Thanks to everyone who contributed to urllib3, your contributions are making a huge difference.&lt;br&gt;
If you'd like to join our little team and start contributing&lt;br&gt;
&lt;a href="https://urllib3.readthedocs.io/en/latest/contributing.html"&gt;we have a guide on how to get started&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>python</category>
      <category>security</category>
    </item>
    <item>
      <title>Optimize Web App Performance with HTTP Header Compression</title>
      <dc:creator>Seth Michael Larson</dc:creator>
      <pubDate>Mon, 09 Dec 2019 16:57:32 +0000</pubDate>
      <link>https://dev.to/sethmlarson/http-header-compression-135b</link>
      <guid>https://dev.to/sethmlarson/http-header-compression-135b</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;This is a &lt;a href="https://sethmlarson.dev/blog/2019-12-06/http-header-compression"&gt;cross-post&lt;/a&gt; from my blog &lt;a href="https://sethmlarson.dev/blog"&gt;&lt;code&gt;Python ♥ HTTP&lt;/code&gt;&lt;/a&gt;. If you enjoy my content and want it sooner you can &lt;a href="https://sethmlarson.dev/blog/rss"&gt;follow me via RSS&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;HTTP header compression is a new feature within HTTP/2 and HTTP/3 that drastically reduces the amount of data that needs to be transported over the wire by building both a static and dynamic encoding of header keys and values into tiny representations.&lt;/p&gt;

&lt;p&gt;Header compression is defined in these two documents:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://tools.ietf.org/html/rfc7541"&gt;RFC 7541 - HPACK: Header Compression for HTTP/2&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://datatracker.ietf.org/doc/draft-ietf-quic-qpack/"&gt;QPACK: Header Compression for HTTP/3 (Draft)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Cloudflare wrote &lt;a href="https://blog.cloudflare.com/hpack-the-silent-killer-feature-of-http-2/"&gt;an article about the technology itself&lt;/a&gt;, which I won't discuss a lot here. If you don't know about header compression in HTTP/2 and HTTP/3 you'd benefit a lot from reading the article before continuing with this post! Instead I'm going to talk about all the fun edge-cases and gotchas and finally some theory-crafting! Woohoo!&lt;/p&gt;

&lt;p&gt;HTTP headers are &lt;strong&gt;by far&lt;/strong&gt; the smallest component of an HTTP request / response cycle. The largest component are typically the HTTP request and response bodies. So prefix this entire post by remembering we're optimizing probably less than 15% of total data transfer over a typical HTTP request / response cycle. Still matters, but it's definitely a "micro" optimization!&lt;/p&gt;

&lt;p&gt;HTTP bodies are already made more efficient by compression techniques available in HTTP/1.x like gzip and brotli (and hopefully zstd soon!). Changes to header representation are one of the real big changes with HTTP/2 and onwards.&lt;/p&gt;

&lt;h2&gt;
  
  
  Edge-cases for Optimizing Header Compression
&lt;/h2&gt;

&lt;p&gt;If your website is fronted by Cloudflare or another CDN, chances are your application is being served over HTTP/2 and (if not already) HTTP/3 soon.&lt;/p&gt;

&lt;p&gt;Here are some tips on how to take advantage of header compression in your applications.&lt;/p&gt;

&lt;h3&gt;
  
  
  Lowercase Characters Encode Smaller
&lt;/h3&gt;

&lt;p&gt;Some time ago I &lt;a href="https://twitter.com/sethmlarson/status/1199866367916355585"&gt;tweeted out this fun-fact about HTTP/2 huffman encoding&lt;/a&gt; which inspired this blog post:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;When using HTTP/2 you can save ~half a bit for every lowercase character you send in a header instead of an uppercase character due to the Huffman encoding weights in HPACK.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A-Z: 6.5 bits per character&lt;/li&gt;
&lt;li&gt;a-z: 6.038 bits per character&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Be responsible, lowercase your headers. 🌈&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Header names already must be all lowercase for HTTP/2, but header values commonly have case-insensitive components. (Check the RFC for that header type if you're unsure!)&lt;/p&gt;

&lt;h3&gt;
  
  
  Fold Duplicate Headers Before Sending
&lt;/h3&gt;

&lt;p&gt;Duplicate header entries are allowed by HTTP but they should be joined with &lt;code&gt;;&lt;/code&gt; to not take up more space than needed. Not a lot of services do this and this is already bad form in HTTP/1.X, just wanted to note it down.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Strict-Transport-Security: max-age=31536000
Strict-Transport-Security: includesubdomains
Strict-Transport-Security: preload
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;should instead be:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Strict-Transport-Security: max-age=31536000; includesubdomains; preload
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The exception to this rule is &lt;code&gt;Set-Cookie&lt;/code&gt; which cannot be folded this way without breaking its semantics.&lt;/p&gt;

&lt;h3&gt;
  
  
  Know the Headers and Values in the Static Table
&lt;/h3&gt;

&lt;p&gt;Knowing the names and values in the static table can help send smaller requests and responses for applications. The static table works via &lt;strong&gt;exact matches&lt;/strong&gt; so any one character being different means that the header can't be optimized.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://tools.ietf.org/html/rfc7541#appendix-A"&gt;HTTP/2 Static Table Reference&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://tools.ietf.org/html/draft-ietf-quic-qpack-11#appendix-A"&gt;HTTP/3 Static Table Reference&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Even when semantically the order of values doesn't matter in any of these cases, they need to be exactly as they are below otherwise HPACK and QPACK can't kick in and replace the header with a reference to the static table:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Accept-Encoding: gzip, deflate, br&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Content-Security-Policy: script-src 'none'; object-src 'none'; base-uri 'none'&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Access-Control-Allow-Methods: get, post, options&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Strict-Transport-Security: max-age=31536000; includesubdomains; preload&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Spacing Around Delimiters
&lt;/h3&gt;

&lt;p&gt;Put a space &lt;code&gt;' '&lt;/code&gt; after every delimiter (e.g. &lt;code&gt;;&lt;/code&gt; and &lt;code&gt;,&lt;/code&gt;) unless you're specifically encoding &lt;code&gt;Content-Type: text/plain;charset=utf-8&lt;/code&gt;. That's the only entry in the static table that &lt;strong&gt;doesn't&lt;/strong&gt; have a space after the &lt;code&gt;;&lt;/code&gt; delimiter.&lt;/p&gt;

&lt;h3&gt;
  
  
  QCRAM → QPACK
&lt;/h3&gt;

&lt;p&gt;Just wanted to note that the &lt;a href="https://tools.ietf.org/html/draft-krasic-quic-qcram-00"&gt;original name for QPACK was QCRAM&lt;/a&gt;. The urgency associated with the word "cram" makes me smile whenever I think about it.&lt;/p&gt;

&lt;p&gt;The name was changed within &lt;a href="https://github.com/quicwg/base-drafts/pull/1164"&gt;this pull request&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Header Compression Theory-crafting
&lt;/h2&gt;

&lt;p&gt;Time to delve into the land of "what-if", and no better time than when HTTP/3 is being finalized. ;)&lt;/p&gt;

&lt;p&gt;My thought on keeping header compression and HTTP/2 + HTTP/3 "simple" is that these protocols are both hard to implement and once implemented probably won't change in any significant way until HTTP/N+1, so trying to get as much right as possible benefits everyone for many years.&lt;/p&gt;

&lt;h3&gt;
  
  
  Separate Huffman Codes for Headers and Values
&lt;/h3&gt;

&lt;p&gt;Header names is a much more constrained charset than header values which have to represent all possible bytes.&lt;/p&gt;

&lt;p&gt;Below is the &lt;a href="https://tools.ietf.org/html/rfc5234"&gt;ABNF grammar&lt;/a&gt; for a header name taken from &lt;a href="https://tools.ietf.org/html/rfc7230"&gt;RFC 7230&lt;/a&gt;.&lt;br&gt;
ABNF grammars are very common in RFCs for describing how protocols look on the wire. If you want to get more into HTTP I recommend learning more!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;field-name     = token
token          = 1*tchar
tchar          = "!" / "#" / "$" / "%"
                 "&amp;amp;" / "'" / "*" / "+"
                 "-" / "." / "^" / "_"
                 "`" / "|" / "~" /
                 DIGIT / ALPHA
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(Basically means one or more alpha-numerics with all the symbols listed above)&lt;/p&gt;

&lt;p&gt;Header names also must be lowercase per the HTTP/2 RFC.&lt;/p&gt;

&lt;p&gt;Given these two data-points you can boil down the total possible number of bytes in a valid HTTP header name to be:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;15 for symbols&lt;/li&gt;
&lt;li&gt;10 for digits&lt;/li&gt;
&lt;li&gt;26 for lowercase characters&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Total: 51 bytes&lt;/strong&gt; instead of 256 for full-coverage!&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This means that creating a Huffman encoding for these 51 bytes can be more compact than having to cover all 256 bytes. Free bandwidth savings!&lt;/p&gt;

&lt;p&gt;If you &lt;strong&gt;have&lt;/strong&gt; to break the RFC and create an invalid HTTP header with bytes outside of the 51 then that header name can be encoded in raw form. Huffman encoding is optional in HTTP/2.&lt;/p&gt;

&lt;p&gt;Having header names and values with separate huffman encodings also allows for different weighting of value huffman codes. Digits are very rare within header names but are very common within header values! That means both shorter header names and shorter header values.&lt;/p&gt;

&lt;h3&gt;
  
  
  More Headers in the Static Table
&lt;/h3&gt;

&lt;p&gt;Headers in the static table are basically free when it comes to size.&lt;br&gt;
HTTP/2 → HTTP/3 increased the size of the static table dramatically. Especially when it comes to having "values" in the static table.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;HTTP/2 HPACK: 61 entries, 14 with values (~23% with values)&lt;/li&gt;
&lt;li&gt;HTTP/3 QPACK (draft 11): 98 entries, 78 with values (&lt;strong&gt;~80% with values&lt;/strong&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Obviously there's some diminishing returns here, but the biggest argument I see against adding almost every HTTP header in common use to the static table is storage size in memory (and the current table isn't that large). See the next section on a way to mitigate this issue!&lt;/p&gt;

&lt;h3&gt;
  
  
  Extensible Static Table
&lt;/h3&gt;

&lt;p&gt;Have "maximum known static table index" be a negotiation parameter. This allows for future expansion of the static table as HTTP grows and new headers are standardized. Also allows for smaller / constrained devices from having to have a large static table in memory.&lt;/p&gt;

&lt;h3&gt;
  
  
  Allow Origins to Manage their own "Static Table"
&lt;/h3&gt;

&lt;p&gt;Vendor-specific headers are excluded from the static table despite their high usage by specific services. (&lt;code&gt;youtube-client-id&lt;/code&gt;, etc). Vendor-specific headers would begin immediately after the largest known static table index. When receiving a new "maximum known static table index" the cached static table would be discarded and started anew. Would have to be some method to manage this static table.&lt;/p&gt;

&lt;p&gt;Really services would probably only need a handful of headers, as almost all services only have a few headers that are their own and then rely on HTTP's standard headers for most functionality.&lt;/p&gt;

&lt;p&gt;Services would need a way to confirm that a client still had the custom static table for their service, maybe this can be a handshake parameter or something more involved?&lt;/p&gt;

&lt;p&gt;This opens up a way for individual clients to be fingerprinted, but it's no worse than caches / cookies (?) and is by definition optional.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>http</category>
      <category>performance</category>
    </item>
    <item>
      <title>Designing for Real-World HTTPS</title>
      <dc:creator>Seth Michael Larson</dc:creator>
      <pubDate>Fri, 06 Dec 2019 16:16:29 +0000</pubDate>
      <link>https://dev.to/sethmlarson/designing-for-real-world-https-48g1</link>
      <guid>https://dev.to/sethmlarson/designing-for-real-world-https-48g1</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;This is a &lt;a href="https://sethmlarson.dev/blog/2019-11-26/designing-for-real-world-https"&gt;cross-post&lt;/a&gt; from my blog &lt;a href="https://sethmlarson.dev/blog"&gt;&lt;code&gt;Python ♥ HTTP&lt;/code&gt;&lt;/a&gt;. If you enjoy my content and want it sooner you can &lt;a href="https://sethmlarson.dev/blog/rss"&gt;follow me via RSS&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Users care about security, privacy, and above all, for their HTTP client library to work.&lt;br&gt;
When TLS or certificate issues get in the way of making requests to a web server&lt;br&gt;
oftentimes users will insecurely configure their HTTP client.&lt;/p&gt;

&lt;p&gt;And who can blame them when they don't have the documentation or tools to make proper&lt;br&gt;
security decisions in a world full of legacy software and mis-configured servers?&lt;/p&gt;

&lt;p&gt;This post discusses how to design an HTTP client for better security and user-experience.&lt;br&gt;
There's a lot of additional / optional readings linked in this post. I encourage you if you&lt;br&gt;
want to learn a whole lot more to take a look at those resources too.&lt;/p&gt;
&lt;h2&gt;
  
  
  What is HTTPS?
&lt;/h2&gt;

&lt;p&gt;HTTPS is defined in &lt;a href="https://tools.ietf.org/html/rfc2818"&gt;RFC 2818&lt;/a&gt; as "HTTP over TLS".&lt;br&gt;
If you're unsure what TLS is, you can read&lt;br&gt;
&lt;a href="https://www.cloudflare.com/learning/ssl/transport-layer-security-tls/"&gt;this explanation by Cloudflare&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  How do users typically solve TLS issues?
&lt;/h2&gt;

&lt;p&gt;If you type "&lt;a href="https://www.google.com/search?q=requests+certificate+verify+failed"&gt;requests certificate verify failed&lt;/a&gt;"&lt;br&gt;
into Google the top result you'll receive is this &lt;a href="https://stackoverflow.com/a/12864892"&gt;StackOverflow answer&lt;/a&gt;&lt;br&gt;
which (among some good advice) mentions &lt;code&gt;verify=False&lt;/code&gt; which disables certificate verification.&lt;/p&gt;

&lt;p&gt;This will certainly allow your request to "succeed" but will also severely degrade the&lt;br&gt;
security of the HTTPS requests being made.&lt;/p&gt;

&lt;p&gt;The warnings against using &lt;code&gt;verify=False&lt;/code&gt; within the StackOverflow answer may persuade some,&lt;br&gt;
but searching &lt;a href="https://github.com/search?l=Python&amp;amp;q=%22verify%3DFalse%22&amp;amp;type=Code"&gt;"&lt;code&gt;verify=False&lt;/code&gt;" in Python code on GitHub&lt;/a&gt;&lt;br&gt;
yields &amp;gt;150,000 results.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Our goal as an interface designer is to keep verification on!&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  What issues do users experience?
&lt;/h2&gt;

&lt;p&gt;Imagine you're trying to interact with an external service as a part of&lt;br&gt;
your job via HTTP and of course, you want to use HTTPS because that's the proper thing to do!&lt;/p&gt;

&lt;p&gt;The website's hostname is &lt;code&gt;tribunnews.com&lt;/code&gt; which just so happens to&lt;br&gt;
be at the time of this writing #41 most visited website in the world.&lt;br&gt;
You'd expect them to have a proper TLS config, right?&lt;/p&gt;

&lt;p&gt;When you make an HTTPS request to the website you receive this error:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;[SSL: UNSUPPORTED_PROTOCOL] unsupported protocol (_ssl.c:1108)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now without using Google, can you figure out what this error means&lt;br&gt;
and what you should do to fix it?&lt;/p&gt;

&lt;p&gt;If you guessed that the server only supports TLSv1.0, you'd be right!&lt;br&gt;
The client library you're using needs to be configured to use TLSv1.0&lt;br&gt;
which is considered an insecure protocol to use nowadays.&lt;/p&gt;

&lt;p&gt;After configuring your client properly (if you're able to!) you make&lt;br&gt;
another request, this time receiving this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate (_ssl.c:1108)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At this point unfortunately you're out of luck, because the website presents a certificate&lt;br&gt;
with two &lt;code&gt;subjectAltName&lt;/code&gt; entries:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;*.maintenis.com&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;maintenis.com&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;... neither of which match our intended target website &lt;code&gt;tribunnews.com&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;When a certificate error occurs &lt;strong&gt;we can't be certain that the web server we're talking to is&lt;br&gt;
the one we intend to be talking to&lt;/strong&gt;, so now what? What would you do in this situation?&lt;/p&gt;

&lt;p&gt;If you guessed using &lt;code&gt;www.tribunnews.com&lt;/code&gt; (???) you'd be correct! Using the &lt;code&gt;www&lt;/code&gt; sub-domain gets you the&lt;br&gt;
website you want with all proper TLS configuration and certificates.&lt;/p&gt;

&lt;p&gt;Browsers are smart and try &lt;code&gt;www&lt;/code&gt; if the host is a privately-registrable domain name and the connection fails&lt;br&gt;
for any reason, so the website works fine for browsers but doesn't work for our HTTP clients... &lt;strong&gt;What a nightmare!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Now try solving these problems under a time crunch and without a deep knowledge&lt;br&gt;
of TLS and Public Key Infrastructure. You can now hopefully see why users disable&lt;br&gt;
security features to mitigate TLS issues.&lt;/p&gt;
&lt;h2&gt;
  
  
  More Examples of HTTPS Issues
&lt;/h2&gt;

&lt;p&gt;Issues when using HTTPS can be categorized into two different groups,&lt;br&gt;
TLS issues and certificate issues. TLS issues are usually to do&lt;br&gt;
with the TLS protocol itself, things like not supporting the same TLS version,&lt;br&gt;
having no ciphers shared between the client and server,&lt;br&gt;
or sending incorrect data during the TLS handshake.&lt;/p&gt;

&lt;p&gt;I've found that certificates issues tend to occur more frequently compared to&lt;br&gt;
TLS protocol issues so I've focused on them below.&lt;/p&gt;

&lt;p&gt;Here is the list of sources that I've looked through to find examples of these issues:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://stackoverflow.com/questions/tagged/python-requests"&gt;Stackoverflow 'python-requests' Tag&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/psf/requests/issues"&gt;requests Issue Tracker&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/urllib3/urllib3/issues"&gt;urllib3 Issue Tracker&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I've also ran TLS handshakes on the top 10,000 domains in the Alexa 1 Million and&lt;br&gt;
kept the results of all domains where the TLS handshake failed when configured&lt;br&gt;
with &lt;a href="https://github.com/certifi/python-certifi"&gt;&lt;code&gt;certifi&lt;/code&gt;&lt;/a&gt; (a common choice for Python HTTP libraries&lt;br&gt;
as a default CA certificate bundle) and with all TLS 1.2+ and all ciphers. &lt;/p&gt;

&lt;p&gt;All of these below issues can be found at least once in the top 10,000 domains.&lt;/p&gt;
&lt;h3&gt;
  
  
  Certificate Doesn't Have a &lt;code&gt;subjectAltName&lt;/code&gt;, only &lt;code&gt;commonName&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;commonName&lt;/code&gt; was deprecated over 19 years ago in&lt;br&gt;
&lt;a href="https://tools.ietf.org/html/rfc2818#section-3.1"&gt;RFC 2818&lt;/a&gt;. Here's the&lt;br&gt;
exact language:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;If a subjectAltName extension of type dNSName is 
present, that MUST be used as the identity. Otherwise,
the (most specific) Common Name field in the Subject
field of the certificate MUST be used. Although the
use of the Common Name is existing practice, it is
deprecated and Certification Authorities are encouraged
to use the dNSName instead.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Which unfortunately says that &lt;code&gt;commonName&lt;/code&gt; must be used if there&lt;br&gt;
are no &lt;code&gt;subjectAltName&lt;/code&gt; &lt;code&gt;dNSName&lt;/code&gt; entries. Meaning it's totally&lt;br&gt;
valid for CAs to keep minting certificates with common names.&lt;/p&gt;

&lt;p&gt;This means we probably need to keep supporting a way to verify&lt;br&gt;
certificates via &lt;code&gt;commonName&lt;/code&gt; for the time being.&lt;/p&gt;

&lt;p&gt;Thankfully browsers and macOS are driving the world of standards&lt;br&gt;
forward by removing support for common name.&lt;br&gt;
&lt;a href="https://support.apple.com/en-us/HT210176"&gt;macOS dropped common name support completely in iOS 13 / macOS 10.15&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;TLS server certificates must present the DNS name of
the server in the Subject Alternative Name extension
of the certificate. DNS names in the CommonName of a
certificate are no longer trusted.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nothing like your website not working on Chrome, Firefox, macOS and iPhones&lt;br&gt;
to make a business want to upgrade their certificate.&lt;/p&gt;
&lt;h3&gt;
  
  
  Certificate is Self-Signed
&lt;/h3&gt;

&lt;p&gt;This can be the case for a real web server, a certificate created for a&lt;br&gt;
single peer-to-peer connection like two devices being manufactured together,&lt;br&gt;
or most likely: a user-generated certificate for integration testing.&lt;/p&gt;

&lt;p&gt;In this case, especially when the certificate is available to the&lt;br&gt;
user and can be verified locally, &lt;a href="https://www.owasp.org/index.php/Certificate_and_Public_Key_Pinning"&gt;Certificate Pinning&lt;/a&gt;&lt;br&gt;
solves this problem! Certificate pinning is where you hard-code the signature&lt;br&gt;
or fingerprint of the certificate you're expecting to be presented&lt;br&gt;
in the handshake and if you receive a certificate with the same&lt;br&gt;
fingerprint then the certificate is automatically trusted. &lt;/p&gt;

&lt;p&gt;Using certificate pinning is a form of&lt;br&gt;
&lt;a href="https://en.wikipedia.org/wiki/Trust_on_first_use"&gt;"Trust on First Use"&lt;/a&gt;&lt;br&gt;
which is nearly as secure as verifying a certificate using PKI and CA certs&lt;br&gt;
except for the very first TLS handshake where you make the first "trust" decision yourself.&lt;/p&gt;

&lt;p&gt;(&lt;strong&gt;NOTE:&lt;/strong&gt; Certificate Signatures / Fingerprints are different from Certificate Thumbprints.&lt;br&gt;
The former is used for cryptographic reasons, the second is for referencing only.)&lt;/p&gt;

&lt;p&gt;The tough part about this feature is that error messages and documentation&lt;br&gt;
don't usually recommend using the feature as an alternative to&lt;br&gt;
disabling certificate verification. It's also non-trivial to calculate&lt;br&gt;
the certificate fingerprint by hand.&lt;/p&gt;

&lt;p&gt;Certificate pinning is currently supported by&lt;br&gt;
&lt;a href="https://urllib3.readthedocs.io/en/latest/reference/index.html#urllib3.connection.VerifiedHTTPSConnection.assert_fingerprint"&gt;&lt;code&gt;urllib3&lt;/code&gt;&lt;/a&gt; and&lt;br&gt;
&lt;a href="https://aiohttp.readthedocs.io/en/stable/client_reference.html#aiohttp.Fingerprint"&gt;&lt;code&gt;aiohttp&lt;/code&gt;&lt;/a&gt;.&lt;br&gt;
Requests doesn't provide a way to natively verify a certificate via a fingerprint.&lt;/p&gt;

&lt;p&gt;Client libraries should use only &lt;code&gt;SHA256&lt;/code&gt; or stronger because &lt;code&gt;MD5&lt;/code&gt; is insecure and &lt;code&gt;SHA1&lt;/code&gt; is on the edge of insecure.&lt;/p&gt;

&lt;p&gt;Client libraries should also ensure users are aware of the pitfalls and dangers associated with&lt;br&gt;
updating the certificate fingerprint or using certificate pinning with certs that are liable to change&lt;br&gt;
frequently like certificates that are short-lived (~less than 6 months to expiry). &lt;/p&gt;
&lt;h3&gt;
  
  
  Certificate &lt;code&gt;subjectAltName&lt;/code&gt; or &lt;code&gt;commonName&lt;/code&gt; Doesn't Match Host
&lt;/h3&gt;

&lt;p&gt;In this case the web server is presenting a certificate that we'd&lt;br&gt;
be able to verify with our cert trust store but the fields for&lt;br&gt;
verifying the hostname aren't the same as the host we're connecting to.&lt;/p&gt;

&lt;p&gt;For this situation the most useful information for the user is&lt;br&gt;
to display what the fields are on the certificate along with the&lt;br&gt;
host that they're connecting to. Many libraries won't show the&lt;br&gt;
certificates &lt;code&gt;subjectAltName&lt;/code&gt; and &lt;code&gt;commonName&lt;/code&gt; entries in the error.&lt;/p&gt;

&lt;p&gt;I've seen this error come up where the service had a unicode hostname&lt;br&gt;
encoded in the certificate with IDNA 2003 because it contained an emoji&lt;br&gt;
(which is invalid in IDNA 2008), but urllib3 no longer supported IDNA 2003&lt;br&gt;
due to security issues with URL parsing.&lt;/p&gt;

&lt;p&gt;WHATWG-URL recommends falling back on IDNA 2003, but this requires a lot of care&lt;br&gt;
to mitigate all security issues related to using the encoding within URLs.&lt;/p&gt;
&lt;h2&gt;
  
  
  Recommendations for HTTP Client Libraries
&lt;/h2&gt;

&lt;p&gt;Above all, think about users when designing an interface to configure security.&lt;br&gt;
An interface where it's easy to create secure configurations will result in&lt;br&gt;
better security for every use-case.&lt;/p&gt;

&lt;p&gt;Here's a list of elements I think result in such an interface:&lt;/p&gt;
&lt;h3&gt;
  
  
  Provide a State-of-the-Art TLS Configuration by Default
&lt;/h3&gt;

&lt;p&gt;90%+ users will be using these defaults so you want them to be the best they can reasonably be.&lt;/p&gt;

&lt;p&gt;At the time of writing this means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;TLSv1.2+ only (&lt;a href="https://www.ssllabs.com/ssl-pulse/"&gt;Over 95% of websites support TLSv1.2+&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://en.wikipedia.org/wiki/Authenticated_encryption"&gt;AEAD Ciphers&lt;/a&gt; (AES-GCM, CHACHA20)&lt;/li&gt;
&lt;li&gt;Key Exchanges that support &lt;a href="https://scotthelme.co.uk/perfect-forward-secrecy/"&gt;Forward Secrecy&lt;/a&gt; (ECDHE, DHE)&lt;/li&gt;
&lt;li&gt;System Certificate Trust Store (This is still an open issue for Python, for now &lt;code&gt;certifi&lt;/code&gt; is a crutch.)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here's how to configure the above in Python via &lt;code&gt;ssl.SSLContext&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;ssl&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;certifi&lt;/span&gt;

&lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ssl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SSLContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ssl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PROTOCOL_TLS&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;verify_mode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ssl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CERT_REQUIRED&lt;/span&gt;
&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;load_verify_locations&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;certifi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;where&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt; &lt;span class="o"&gt;|=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;ssl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OP_NO_SSLv2&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="n"&gt;ssl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OP_NO_SSLv3&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="n"&gt;ssl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OP_NO_TLSv1&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="n"&gt;ssl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OP_NO_TLSv1_1&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="n"&gt;ssl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OP_NO_COMPRESSION&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;set_ciphers&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;":"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
    &lt;span class="s"&gt;"ECDHE+AESGCM"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;"ECDHE+CHACHA20"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;"DHE+AESGCM"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;"DHE+CHACHA20"&lt;/span&gt;
&lt;span class="p"&gt;]))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Don't Allow Users to Disable Certificate Verification
&lt;/h3&gt;

&lt;p&gt;Especially for HSTS domains, where this is a requirement to be compliant&lt;br&gt;
with &lt;a href="https://tools.ietf.org/html/rfc6797"&gt;RFC 6797&lt;/a&gt;.&lt;br&gt;
Being able to turn off certificate verification results in insecure&lt;br&gt;
software. Using cert pinning is a more secure alternative to disabling verification.&lt;/p&gt;

&lt;h3&gt;
  
  
  Provide these Features for Configuring TLS
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Minimum and maximum supported version of TLS.&lt;br&gt;
Recommend strongly that users don't set a maximum version&lt;br&gt;
unless they're targeting one specific version on a single service.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;TLS ciphers should be added / removed depending on&lt;br&gt;
the minimum and maximum TLS version. I don't think direct&lt;br&gt;
access to configuring ciphers should be necessary but make&lt;br&gt;
sure you cover all the common ciphers and key-exchanges.&lt;br&gt;
Look at what Firefox uses as their default for an example.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;An interface to provide a pre-configured &lt;code&gt;SSLContext&lt;/code&gt; is optional,&lt;br&gt;
in my opinion if an interface wrapping TLS is good enough providing&lt;br&gt;
an interface for &lt;code&gt;SSLContext&lt;/code&gt; only allows for disabling security features.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Ability to specify a non-default certificate bundle&lt;br&gt;
or directory of certificates to verify against.&lt;br&gt;
Unfortunately it's optimal to support both an&lt;br&gt;
"additive" method and a "replace" method.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Additive&lt;/strong&gt; is useful when the rest of your trust store&lt;br&gt;
is still needed like in the case of a proxy certificate: you&lt;br&gt;
still want to use your actual trust store to verify&lt;br&gt;
external services.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Replace&lt;/strong&gt; is useful when you want to use a singular certificate&lt;br&gt;
for the requests you're making and don't want to accidentally&lt;br&gt;
trust a service outside that trust store.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I'd say that the 'additive' use-case is more common with typical&lt;br&gt;
users but supporting both use-cases is important. I need to do&lt;br&gt;
more thinking on this about what a proper interface would look like.&lt;/p&gt;

&lt;h3&gt;
  
  
  Support Certificate Pinning as a means of verification
&lt;/h3&gt;

&lt;p&gt;Provide documentation on how to use it, how to update the fingerprint,&lt;br&gt;
and what using certificate pinning means for security.&lt;/p&gt;

&lt;h3&gt;
  
  
  Recommend Solutions for Local Testing
&lt;/h3&gt;

&lt;p&gt;For local testing recommend users use &lt;a href="https://github.com/python-trio/trustme"&gt;&lt;code&gt;trustme&lt;/code&gt;&lt;/a&gt;&lt;br&gt;
or &lt;a href="https://github.com/FiloSottile/mkcert"&gt;&lt;code&gt;mkcert&lt;/code&gt;&lt;/a&gt; to generate real&lt;br&gt;
certificates so that HTTPS and TLS function the same locally&lt;br&gt;
as they would in the real world.  This removes the need for disabling verification&lt;br&gt;
during testing or using pinning on a self-signed cert.&lt;/p&gt;

&lt;h3&gt;
  
  
  Provide Better Error Messages and Documentation
&lt;/h3&gt;

&lt;p&gt;Error messages should contain all information&lt;br&gt;
they need to make proper security decisions. This includes information&lt;br&gt;
from the certificate. This may require you to use &lt;code&gt;getpeercert(binary_form=True)&lt;/code&gt;&lt;br&gt;
and parse with a library like &lt;a href="https://github.com/wbond/asn1crypto"&gt;&lt;code&gt;asn1crypto&lt;/code&gt;&lt;/a&gt;&lt;br&gt;
because Python doesn't give you nice parsed certificates unless cert&lt;br&gt;
verification is enabled.&lt;/p&gt;

&lt;p&gt;Document common TLS and certificate issues and the recommended&lt;br&gt;
way for mitigating those issues. If possible, provide custom error&lt;br&gt;
types for common issues along with pointers to official documentation&lt;br&gt;
to prevent StackOverflow from recommending insecure configurations&lt;br&gt;
to your users.&lt;/p&gt;

&lt;h3&gt;
  
  
  Allow Configuring TLS via Environment Variables
&lt;/h3&gt;

&lt;p&gt;Usually HTTP clients are buried a layer or more deep within an library&lt;br&gt;
or tool being used and may sometimes not provide the means to configure&lt;br&gt;
the HTTP client library properly. Allowing environment variables to&lt;br&gt;
configure the library fixes this issue for users.&lt;/p&gt;

&lt;p&gt;Did you have a scenario where the above doesn't work well or you want&lt;br&gt;
to discuss this subject more? I'd love to hear about it, please let me know&lt;br&gt;
by &lt;a href="https://twitter.com/sethmlarson"&gt;tagging me Twitter&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>security</category>
      <category>python</category>
      <category>tls</category>
    </item>
    <item>
      <title>Sponsored Work on urllib3</title>
      <dc:creator>Seth Michael Larson</dc:creator>
      <pubDate>Mon, 11 Nov 2019 13:52:40 +0000</pubDate>
      <link>https://dev.to/sethmlarson/sponsored-work-on-urllib3-5aip</link>
      <guid>https://dev.to/sethmlarson/sponsored-work-on-urllib3-5aip</guid>
      <description>&lt;p&gt;&lt;a href="https://github.com/urllib3/urllib3"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--_AZiFJ__--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/urllib3/urllib3/master/docs/images/banner.svg" alt="urllib3"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;urllib3 is a popular HTTP client library in Python for which I've been a maintainer for the past 2 years. Some months ago the urllib3 maintainers were approached by &lt;a href="https://gitcoin.co"&gt;GitCoin&lt;/a&gt; with a &lt;a href="https://gitcoin.co/grants/65/urllib3"&gt;grant opportunity&lt;/a&gt; for funded work on urllib3. In this post I'll be documenting the work that I've completed as a result of this grant.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/pquentin/"&gt;Quentin Pradet&lt;/a&gt; also wrote about his experience working on the same grant. &lt;a href="https://quentin.pradet.me/blog/ive-been-paid-to-work-on-open-source.html"&gt;Check out his blog post here&lt;/a&gt;. 🎉&lt;/p&gt;

&lt;p&gt;I focused my development efforts on urllib3's URL parser which was recently upgraded to be RFC 3986 compliant with the &lt;a href="https://github.com/python-hyper/rfc3986/"&gt;&lt;code&gt;rfc3986&lt;/code&gt;&lt;/a&gt; package.&lt;/p&gt;

&lt;p&gt;It turns out that the package is a little too heavy for platforms with fewer CPU and memory resources like Raspberry Pi and the import time suffered due to the amount of regular expression parsing occurring within &lt;code&gt;rfc3986&lt;/code&gt;. The effects caused &lt;code&gt;urllib3&lt;/code&gt; to take &lt;a href="https://github.com/urllib3/urllib3/issues/1590"&gt;800ms to import instead of 250ms&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I read the RFC and with the figured out what components of the package we needed and re-implemented them simpler and with less complicated regular expressions in order to avoid the expensive compilation while still maintaining the level of correctness we needed to provide a secure URL parser. &lt;/p&gt;

&lt;p&gt;It turns out that we don't need to strictly parse the &lt;code&gt;username&lt;/code&gt;, &lt;code&gt;password&lt;/code&gt;, &lt;code&gt;path&lt;/code&gt;, &lt;code&gt;query&lt;/code&gt; or &lt;code&gt;fragment&lt;/code&gt; components, instead we split on the component delimiters and then percent-encode all invalid characters within the component because all of the above components accept percent-encoded values.&lt;/p&gt;

&lt;p&gt;Basically the only components that we strictly parse are the scheme, host and port. All other components we accept the characters that are passed to urllib3 and percent-encode them in order to avoid CRLF-injection and unicode-based attacks that originally spurred the development of an RFC 3986 compliant URL parser for urllib3.&lt;/p&gt;

&lt;p&gt;This allows us to continue to support "technically" incorrect URLs in a safe way that results in better usability while still secure. Unfortunately the &lt;code&gt;host&lt;/code&gt; component is the most complex and still needs some work for me to be completely happy with it.&lt;/p&gt;

&lt;p&gt;The second minor discovery I made was that our &lt;code&gt;ConnectionPool.request()&lt;/code&gt; method didn't apply the same URL parsing logic as the top-level &lt;code&gt;PoolManager.request()&lt;/code&gt; and so would be inconsistent. I added the logic to &lt;code&gt;ConnectionPool.request()&lt;/code&gt; for relative URLs.&lt;/p&gt;

&lt;p&gt;List of Pull Requests made as a result of the Grant:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/urllib3/urllib3/pull/1647"&gt;Pull #1647&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/urllib3/urllib3/pull/1673"&gt;Pull #1673&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/urllib3/urllib3/pull/1692"&gt;Pull #1692&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/urllib3/urllib3/pull/1732"&gt;Pull #1732&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The work I've done as a result of this grant has inspired me to begin working on a URL parser called &lt;a href="https://github.com/urllib3/urllib3/issues/1590"&gt;&lt;code&gt;irl&lt;/code&gt;&lt;/a&gt; specifically targeting simplicity, correctness, usability, and an interface that lends itself to being used by HTTP clients.&lt;/p&gt;

&lt;p&gt;I'd like to thank everyone who donated funds to GitCoin to support my work on this Grant. ❤️&lt;/p&gt;

</description>
      <category>python</category>
      <category>devjournal</category>
    </item>
    <item>
      <title>Python Data Streaming to Google Cloud Storage with Resumable Uploads</title>
      <dc:creator>Seth Michael Larson</dc:creator>
      <pubDate>Sun, 18 Mar 2018 13:44:58 +0000</pubDate>
      <link>https://dev.to/sethmlarson/python-data-streaming-to-google-cloud-storage-with-resumable-uploads-458h</link>
      <guid>https://dev.to/sethmlarson/python-data-streaming-to-google-cloud-storage-with-resumable-uploads-458h</guid>
      <description>&lt;p&gt;A few days ago I spent a large chunk of my afternoon working on implementing memory-efficient data streaming to Google Cloud Storage (GCS) from a Python runtime.&lt;/p&gt;

&lt;p&gt;There were several roadblocks along the way and I'd like to create the documentation that I wish I could find while working on the issue.&lt;/p&gt;

&lt;p&gt;This article uses Python 3.6.4 but can be adapted for other Python versions.&lt;/p&gt;

&lt;h2&gt;
  
  
  GCS support in &lt;code&gt;google-cloud&lt;/code&gt; Module
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://googlecloudplatform.github.io/google-cloud-python/"&gt;&lt;code&gt;google-cloud&lt;/code&gt;&lt;/a&gt; package is a giant collection of modules that can be used to interface with all of the Google Cloud Platform services so it's a great place to start.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;python -m pip install -U google-cloud&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Within the &lt;code&gt;google-cloud&lt;/code&gt; package is a module called &lt;a href="https://googlecloudplatform.github.io/google-cloud-python/latest/storage/client.html"&gt;&lt;code&gt;google.cloud.storage&lt;/code&gt;&lt;/a&gt; which deals with all things GCS.&lt;/p&gt;

&lt;p&gt;I downloaded and setup my &lt;code&gt;GOOGLE_APPLICATION_CREDENTIALS&lt;/code&gt; locally and opened up a Python console to test out some of the functionality. I was able to quickly connect to GCS, create a Bucket, create a Blob, and &lt;a href="https://googlecloudplatform.github.io/google-cloud-python/latest/storage/blobs.html#google.cloud.storage.blob.Blob.upload_from_string"&gt;upload binary data to the Blob&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;google.cloud&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;storage&lt;/span&gt;

&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;storage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;bucket&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;create_bucket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'test-bucket'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;blob&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;blob&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'test-blob'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;blob&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;upload_from_string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sa"&gt;b&lt;/span&gt;&lt;span class="s"&gt;'x'&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1024&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;content_type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'application/octet-stream'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One thing I immediately noticed was that for building &lt;a href="https://github.com/Armonaut"&gt;Armonaut&lt;/a&gt; my use-case would be progressively streaming output to GCS without saving the output to the file-system of the compute instance. There had to be a way to stream data rather than upload it all in one go.&lt;/p&gt;

&lt;h2&gt;
  
  
  Resumable Uploads to the Rescue!
&lt;/h2&gt;

&lt;p&gt;The initial research I did uncovered &lt;a href="https://cloud.google.com/storage/docs/json_api/v1/how-tos/resumable-upload"&gt;Resumable Uploads&lt;/a&gt; as an option for Google Cloud Storage. From their description it says that they have the following use-cases:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You are uploading a large file.&lt;/li&gt;
&lt;li&gt;The chances of network failure are high.&lt;/li&gt;
&lt;li&gt;You don't know the size of the file when the upload starts.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Reasons #1 and #3 both applied to my use-case so I started investigating further.&lt;/p&gt;

&lt;p&gt;I searched the &lt;code&gt;google-cloud&lt;/code&gt; documentation for a mention of resumable uploads which yielded the &lt;a href="https://googlecloudplatform.github.io/google-cloud-python/latest/storage/blobs.html#google.cloud.storage.blob.Blob.create_resumable_upload_session"&gt;&lt;code&gt;Blob.create_resumable_upload_session()&lt;/code&gt;&lt;/a&gt; method. This method starts a Resumable Upload and returns a URL.&lt;/p&gt;

&lt;h2&gt;
  
  
  Resumable Media Package
&lt;/h2&gt;

&lt;p&gt;The set of interactions that must occur for a Resumable Upload to complete successfully were quite complex and I suspected there was already a package that handles this exchange. I found the &lt;a href="https://googlecloudplatform.github.io/google-resumable-media-python/latest/"&gt;&lt;code&gt;google-resumable-media&lt;/code&gt;&lt;/a&gt; package with a bit of Googling. ;-)&lt;/p&gt;

&lt;p&gt;&lt;code&gt;python -m pip install -U google-resumable-media&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The key part of this package I was interested in is the &lt;a href="https://googlecloudplatform.github.io/google-resumable-media-python/latest/google.resumable_media.requests.upload.html#google.resumable_media.requests.upload.ResumableUpload"&gt;&lt;code&gt;google.resumable_media.requests.ResumableUpload&lt;/code&gt;&lt;/a&gt; class which takes an authorized transport and then allows you to upload data in chunks and recover when errors are detected.&lt;/p&gt;

&lt;p&gt;So far this was the code I was working with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;io&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;google.auth.transport.requests&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;AuthorizedSession&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;google.cloud&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;storage&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;google.resumable_media.requests&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ResumableUpload&lt;/span&gt;

&lt;span class="n"&gt;chunk_size&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;256&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1024&lt;/span&gt;  &lt;span class="c1"&gt;# Minimum chunk-size supported by GCS
&lt;/span&gt;&lt;span class="n"&gt;stream&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BytesIO&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;b&lt;/span&gt;&lt;span class="s"&gt;'x'&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1024&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1024&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;  &lt;span class="c1"&gt;# Fake data stream
&lt;/span&gt;
&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;storage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;bucket&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bucket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'test-bucket'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;blob&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;blob&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'test-blob'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Create a Resumable Upload
&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;blob&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;create_resumable_upload_session&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;content_type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'application/octet-stream'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Pass the URL off to the ResumableUpload object
&lt;/span&gt;&lt;span class="n"&gt;upload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ResumableUpload&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;upload_url&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;chunk_size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;chunk_size&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;transport&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;AuthorizedSession&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;credentials&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_credentials&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Start using the Resumable Upload
&lt;/span&gt;&lt;span class="n"&gt;upload&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;initiate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;transport&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;transport&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;content_type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'application/octet-stream'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;metadata&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;'name'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;blob&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Problem was I was getting an error on &lt;code&gt;upload.initiate()&lt;/code&gt;. It was complaining that there was no &lt;code&gt;Location&lt;/code&gt; header on the response. I investigated this issue and found that &lt;code&gt;create_resumable_upload_session()&lt;/code&gt; was doing the work of &lt;code&gt;upload.initiate()&lt;/code&gt;! I removed that step and instead used the API endpoint provided in the Resumable Upload documentation.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Create a Resumable Upload
&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s"&gt;'https://www.googleapis.com/upload/storage/v1/b/'&lt;/span&gt;
    &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;bucket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/o?uploadType=resumable'&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;upload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ResumableUpload&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;upload_url&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;chunk_size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;chunk_size&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;transport&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;AuthorizedSession&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;credentials&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_credentials&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Start using the Resumable Upload
&lt;/span&gt;&lt;span class="n"&gt;upload&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;initiate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;transport&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;transport&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;content_type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'application/octet-stream'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;metadata&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;'name'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;blob&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This snippet worked to start a Resumable Upload! Now to stream the data.&lt;/p&gt;

&lt;h2&gt;
  
  
  Streaming Data and &lt;code&gt;stream_last=False&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;ResumableUpload&lt;/code&gt; object has a method called &lt;a href="https://googlecloudplatform.github.io/google-resumable-media-python/latest/google.resumable_media.requests.upload.html#google.resumable_media.requests.upload.ResumableUpload.transmit_next_chunk"&gt;&lt;code&gt;transmit_next_chunk&lt;/code&gt;&lt;/a&gt; which tells the upload that the next chunk may be uploaded. While reading the documentation about this method I found &lt;code&gt;stream_final&lt;/code&gt; which was a parameter of the &lt;a href="https://googlecloudplatform.github.io/google-resumable-media-python/latest/google.resumable_media.requests.upload.html#google.resumable_media.requests.upload.ResumableUpload.transmit_next_chunk"&gt;&lt;code&gt;ResumableUpload.initiate&lt;/code&gt;&lt;/a&gt; method.&lt;/p&gt;

&lt;p&gt;I found that if &lt;code&gt;stream_final&lt;/code&gt; is set to &lt;code&gt;False&lt;/code&gt; then the &lt;code&gt;ResumableUpload&lt;/code&gt; will detect the "end" of the stream when a chunk is transmitted that is less than the &lt;code&gt;chunk_size&lt;/code&gt; parameter set in its constructor. This meant that to stream an unknown amount of data that each chunk would have to be &amp;gt;256KiB and would have to buffer output until that size was reached to be trasmitted.&lt;/p&gt;

&lt;h4&gt;
  
  
  Enjoying this post? &lt;a href="https://sethmlarson.dev/blog"&gt;Check out my Dev Blog for more.&lt;/a&gt;
&lt;/h4&gt;

&lt;h2&gt;
  
  
  Putting it All Together
&lt;/h2&gt;

&lt;p&gt;After getting a simple example working I created a class that handles a single stream of unknown length data being uploaded to a blob progressively and recovers from network errors if detected.&lt;/p&gt;

&lt;p&gt;To accomplish this I implemented an object that both buffered data and had a file-like interface in order for it to be used by &lt;code&gt;ResumableUpload&lt;/code&gt; as a &lt;code&gt;stream&lt;/code&gt; and be passed into other functions that require file-like objects for writing data.&lt;/p&gt;

&lt;p&gt;Here is my final implementation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;google.auth.transport.requests&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;AuthorizedSession&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;google.resumable_media&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;common&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;google.cloud&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;storage&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;GCSObjectStreamUpload&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;object&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
            &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;storage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;bucket_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;blob_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;chunk_size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;256&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1024&lt;/span&gt;
        &lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_bucket&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bucket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bucket_name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_blob&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_bucket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;blob&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;blob_name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_buffer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;b&lt;/span&gt;&lt;span class="s"&gt;''&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_buffer_size&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_chunk_size&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;chunk_size&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_read&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;

        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_transport&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;AuthorizedSession&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;credentials&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_credentials&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_request&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;  &lt;span class="c1"&gt;# type: requests.ResumableUpload
&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__enter__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__exit__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;exc_type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;exc_type&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s"&gt;'https://www.googleapis.com/upload/storage/v1/b/'&lt;/span&gt;
            &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_bucket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/o?uploadType=resumable'&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_request&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ResumableUpload&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;upload_url&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;chunk_size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_chunk_size&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;initiate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;transport&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_transport&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;content_type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'application/octet-stream'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;stream_final&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;metadata&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;'name'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_blob&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;stop&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;transmit_next_chunk&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_transport&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;data_len&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_buffer_size&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;data_len&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_buffer&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;
        &lt;span class="k"&gt;del&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;
        &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_buffer_size&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_chunk_size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;transmit_next_chunk&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_transport&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;common&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;InvalidResponse&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;recover&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_transport&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;data_len&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;read&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;chunk_size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# I'm not good with efficient no-copy buffering so if this is
&lt;/span&gt;        &lt;span class="c1"&gt;# wrong or there's a better way to do this let me know! :-)
&lt;/span&gt;        &lt;span class="n"&gt;to_read&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;chunk_size&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_buffer_size&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;memview&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;memoryview&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_buffer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_buffer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;memview&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;to_read&lt;/span&gt;&lt;span class="p"&gt;:].&lt;/span&gt;&lt;span class="n"&gt;tobytes&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_read&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;to_read&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_buffer_size&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt; &lt;span class="n"&gt;to_read&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;memview&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="n"&gt;to_read&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;tobytes&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;tell&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_read&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The class can be used like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;storage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;GCSObjectStreamUpload&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bucket&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'test-bucket'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;blob&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'test-blob'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1024&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;b&lt;/span&gt;&lt;span class="s"&gt;'x'&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1024&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Thanks for reading!&lt;/p&gt;

</description>
      <category>gcp</category>
      <category>cloud</category>
      <category>storage</category>
      <category>python</category>
    </item>
    <item>
      <title>Mashpack - Beating Messagepack at its own Game</title>
      <dc:creator>Seth Michael Larson</dc:creator>
      <pubDate>Tue, 23 Jan 2018 15:01:10 +0000</pubDate>
      <link>https://dev.to/sethmlarson/mashpack---beating-messagepack-at-its-own-game-3p18</link>
      <guid>https://dev.to/sethmlarson/mashpack---beating-messagepack-at-its-own-game-3p18</guid>
      <description>&lt;p&gt;&lt;a href="https://github.com/SethMichaelLarson/mashpack"&gt;Mashpack&lt;/a&gt; is a JSON-object serialization format that is a 'dialect' of the popular format &lt;a href="https://msgpack.org/"&gt;Messagepack&lt;/a&gt;. I read &lt;a href="https://github.com/msgpack/msgpack/blob/master/spec.md"&gt;Messagepacks specification&lt;/a&gt; and noticed a few details that I thought could be improved so I challenged myself to 'beat' Messagepack. Here are my results:&lt;/p&gt;

&lt;h1&gt;
  
  
  Key Design Differences
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;Single-Byte Prefix Reordering&lt;/li&gt;
&lt;li&gt;Typed Arrays&lt;/li&gt;
&lt;li&gt;8-bit Array Type&lt;/li&gt;
&lt;li&gt;Extensions&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Single-byte Header Data Types
&lt;/h2&gt;

&lt;p&gt;Both Mashpack and Messagepack use bit-prefix tricks to pack certain small data types into a single byte while still allowing enough prefix space to support all the other data types we need to ensure an efficient use of space.&lt;/p&gt;

&lt;p&gt;Both specifications use of single-byte data types matters quite a bit as it is the hope that most serialized objects will end up as one of the single-byte header data types due to their small header footprint so having a large and common range of values is invaluable.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Spec&lt;/th&gt;
&lt;th&gt;Single-byte Header Data Types&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Mashpack&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;MAPP&lt;/code&gt;, &lt;code&gt;STRP&lt;/code&gt;, &lt;code&gt;MARRAYP&lt;/code&gt;, &lt;code&gt;INTP&lt;/code&gt;, &lt;code&gt;NINTP&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Messagepack&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;positive fixint&lt;/code&gt;, &lt;code&gt;fixstr&lt;/code&gt;, &lt;code&gt;negative fixint&lt;/code&gt;, &lt;code&gt;fixmap&lt;/code&gt;, &lt;code&gt;fixarray&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;In Mashpack the string and map data types are prioritized with&lt;br&gt;
the largest amount of space. Compare this to Messagepack which prioritizes&lt;br&gt;
positive integers quite heavily and all other data types have smaller ranges as a result.&lt;/p&gt;

&lt;p&gt;See the tables below comparing the two specs:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Data Type&lt;/th&gt;
&lt;th&gt;Range in Mashpack&lt;/th&gt;
&lt;th&gt;Range in Messagepack&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;MAPP&lt;/code&gt;/&lt;code&gt;fixmap&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;0 to 63 key-value pairs&lt;/td&gt;
&lt;td&gt;0 to 15 key-value pairs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;STRP&lt;/code&gt;/&lt;code&gt;fixstr&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;0 to 63 encoded bytes&lt;/td&gt;
&lt;td&gt;0 to 31 encoded bytes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;MARRAYP&lt;/code&gt;/&lt;code&gt;fixarray&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;0 to 31 elements&lt;/td&gt;
&lt;td&gt;0 to 15 elements&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;NINTP&lt;/code&gt;/&lt;code&gt;negative fixint&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;-1 to -32&lt;/td&gt;
&lt;td&gt;-1 to -32&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;INTP&lt;/code&gt;/&lt;code&gt;positive fixint&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;0 to 31&lt;/td&gt;
&lt;td&gt;0 to 127&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;See &lt;a href="https://github.com/SethMichaelLarson/mashpack#specification"&gt;all Mashpack's data types in one table&lt;/a&gt; for more information.&lt;/p&gt;
&lt;h2&gt;
  
  
  Typed and Mixed Arrays
&lt;/h2&gt;

&lt;p&gt;Mashpack adds a new data type that isn't present in Messagepack called the 'typed array' or just &lt;code&gt;ARRAY*&lt;/code&gt;. This in contrast to a 'mixed array' (&lt;code&gt;MARRAY*&lt;/code&gt;) requires that all objects within the array be the same data type. Objects which have different headers but are in the same family can be 'upgraded' to a larger data type long as the resulting typed array is smaller than having a mixed array.&lt;/p&gt;

&lt;p&gt;An example is a list of integers, one of which would be encoded into an &lt;code&gt;INTP&lt;/code&gt; type and all others being converted into &lt;code&gt;INT8&lt;/code&gt; would be converted into an &lt;code&gt;ARRAY8[INT8]&lt;/code&gt;. See below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;object = [1, 255, 255]
could be naively encoded into:
MARRAYP([INTP(1), INT8(255), INT8(255)]) = 7 bytes

but converting the INTP(1) into INT8(1) we can take
advantage of typed arrays to end up with this:

ARRAY8[INT8]([1, 255, 255]) = 6 bytes
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  Adding an 8-bit Array Type
&lt;/h2&gt;

&lt;p&gt;Despite having an unused data type id (&lt;code&gt;0xC1&lt;/code&gt;) Messagepack does not define&lt;br&gt;
an 8-bit array type. This in addition to the smaller range of fixarray&lt;br&gt;
being only 15 elements means that arrays with 16 elements in Messagepack&lt;br&gt;
use 3 bytes of header rather than 1 in Mashpack using &lt;code&gt;MARRAYP&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Larger Strings means Unicode Friendly
&lt;/h2&gt;

&lt;p&gt;Unicode brought a lot of harmony to what used to be a really hard problem with universally rendering bytes into strings. One of the necessary evils of unicode is that not all characters are 8 bits meaning that every string that's not ASCII-only requires additional space to store. Mashpack being able to store over double the number of encoded bytes within a one-byte string helps with this problem.&lt;/p&gt;

&lt;p&gt;Messagepack's Python implementation also seems to have had a strange relationship with its string data types due to Python 2. The current Mashpack Python implementation supports Python 3+ only and only allows UTF-8 encoding for its string type.&lt;/p&gt;

&lt;h2&gt;
  
  
  Larger Strings means URLs are Mostly Short Too!
&lt;/h2&gt;

&lt;p&gt;A lot of APIs nowadays have helpful 'explorative' URLS to hint API users about additional content. This will almost always take the form of a complete URL which are typically quite long! Almost always longer than 31 bytes. Having a larger one-byte string helps solve this problem. See the compression performance comparison section.&lt;/p&gt;

&lt;h2&gt;
  
  
  Extensions
&lt;/h2&gt;

&lt;p&gt;Mashpack defines three extension data type sizes (&lt;code&gt;EXT8&lt;/code&gt;, &lt;code&gt;EXT16&lt;/code&gt;, and &lt;code&gt;EXT32&lt;/code&gt;) while Messagepack defines 8 (&lt;code&gt;fixint 1&lt;/code&gt;, &lt;code&gt;fixint 2&lt;/code&gt;, &lt;code&gt;fixint 4&lt;/code&gt;, &lt;code&gt;fixint 8&lt;/code&gt;, &lt;code&gt;fixint 16&lt;/code&gt;, &lt;code&gt;ext8&lt;/code&gt;, &lt;code&gt;ext16&lt;/code&gt;, and &lt;code&gt;ext32&lt;/code&gt;)&lt;/p&gt;

&lt;p&gt;Extensions work very similarly in Messagepack and Mashpack in that they have an extension code and data section. The extension code is an unsigned integer between 0 and 255 that denotes what extension is being used. The difference between Messagepack and Mashpack's extensions is that the fixed extension type for Messagepack defines 'fixed' extensions that are exactly 1, 2, 4, 8, or 16 bytes long. This allows for 2-bytes of header for extensions with smaller data sections.&lt;/p&gt;

&lt;p&gt;Mashpack only allows a 3-byte header minimum, so small data extensions suffer slightly storage-wise under Mashpack. When using a data section 17 bytes or longer extensions pack the same under Mashpack and Messagepack.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fewer Prefix Tiers
&lt;/h2&gt;

&lt;p&gt;Mashpack's prefix tiering (2x2-&amp;gt;3x3-&amp;gt;31x8) having only three layers compared to Messagepack's prefix tiering (1x1-&amp;gt;2x3-&amp;gt;2x4-&amp;gt;31x8) means Mashpack will only use a maximum of 3 comparisons to determine a packed objects type compared to 4 for Messagepack.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fast Unpacking of Positive Fixint
&lt;/h2&gt;

&lt;p&gt;Messagepack's positive fixint type being prefixed as a prefix of &lt;code&gt;0xxxxxxx&lt;/code&gt; allows the integer to be read directly from the byte it resides in without masking.&lt;/p&gt;

&lt;p&gt;Mashpack doesn't have this property but only requires one bit-mask operation to make up for it. This difference was a necessary casualty in ensuring better bit prefixes and ranges for other objects.&lt;/p&gt;

&lt;p&gt;Both Mashpack and Messagepack's &lt;code&gt;NINTP&lt;/code&gt; and negative fixint have this property for negative numbers with a prefix of &lt;code&gt;111xxxxx&lt;/code&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  Performance
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Compression Differences
&lt;/h2&gt;

&lt;p&gt;Here's a few small, medium, and large objects for comparing compression between Mashpack and Messagepack. Lower is better.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Object&lt;/th&gt;
&lt;th&gt;Mashpack Size&lt;/th&gt;
&lt;th&gt;Messagepack Size&lt;/th&gt;
&lt;th&gt;Difference&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;127&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;+1 (+50%)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;[1] * 16&lt;/td&gt;
&lt;td&gt;17&lt;/td&gt;
&lt;td&gt;19&lt;/td&gt;
&lt;td&gt;-2 (-10.5%)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/SethMichaelLarson/mashpack"&gt;URL&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;46&lt;/td&gt;
&lt;td&gt;48&lt;/td&gt;
&lt;td&gt;-2 (-4.1%)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;[127] * 100&lt;/td&gt;
&lt;td&gt;103&lt;/td&gt;
&lt;td&gt;101&lt;/td&gt;
&lt;td&gt;+2 (+1.9%)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;[255] * 100&lt;/td&gt;
&lt;td&gt;103&lt;/td&gt;
&lt;td&gt;203&lt;/td&gt;
&lt;td&gt;-100 (-49.2%)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://developer.github.com/v3/activity/events/types/#webhook-payload-example-26"&gt;GitHub Push Event&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;6091&lt;/td&gt;
&lt;td&gt;6192&lt;/td&gt;
&lt;td&gt;-101 (-1.6%)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Speed Differences
&lt;/h2&gt;

&lt;p&gt;Here's a few small and large objects that were processed 10,000 times to test differences in packing and unpacking speed between Mashpack and Messagepack. Lower is better.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;NOTE:&lt;/strong&gt; These speed comparisons were done on an Intel i5-6400 4x2.7GHz with 16 GB RAM and using the Python 'fallback' version of Messagepack as there is no Cython implementation of Mashpack yet. More benchmarks will be coming to compare Cython implementations later.&lt;/p&gt;

&lt;h3&gt;
  
  
  Packing Objects
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Object&lt;/th&gt;
&lt;th&gt;Mashpack Time&lt;/th&gt;
&lt;th&gt;Messagepack Time&lt;/th&gt;
&lt;th&gt;Difference&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;[1, 1, 1, 1, 1]&lt;/td&gt;
&lt;td&gt;0.22 seconds&lt;/td&gt;
&lt;td&gt;0.31 seconds&lt;/td&gt;
&lt;td&gt;-0.09 seconds&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://developer.github.com/v3/activity/events/types/#webhook-payload-example-26"&gt;GitHub Push Event&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;42.9 seconds&lt;/td&gt;
&lt;td&gt;59.4 seconds&lt;/td&gt;
&lt;td&gt;-16.5 seconds&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Unpacking Objects
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Object&lt;/th&gt;
&lt;th&gt;Mashpack Time&lt;/th&gt;
&lt;th&gt;Messagepack Time&lt;/th&gt;
&lt;th&gt;Difference&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;[1, 1, 1, 1, 1]&lt;/td&gt;
&lt;td&gt;0.15 seconds&lt;/td&gt;
&lt;td&gt;0.15 seconds&lt;/td&gt;
&lt;td&gt;0 seconds&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://developer.github.com/v3/activity/events/types/#webhook-payload-example-26"&gt;GitHub Push Event&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;8.39 seconds&lt;/td&gt;
&lt;td&gt;9.72 seconds&lt;/td&gt;
&lt;td&gt;-1.33 seconds&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h1&gt;
  
  
  How can I Help or Contribute?
&lt;/h1&gt;

&lt;p&gt;Mashpack is a brand new specification! Its got plenty left to do before it's cemented into a standard. This is always the most fun part of projects, so I want to share that with everyone who's interested. :)&lt;/p&gt;

&lt;p&gt;Everyone that contributes to the Mashpack repository will receive Collaborator for the repository as I would like this to be a project for the community. All changes require code review by one other Collaborator and any Code Owners.&lt;/p&gt;

&lt;h2&gt;
  
  
  New to Open Source or Python?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/SethMichaelLarson/mashpack/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22"&gt;There are some issues that I've marked with &lt;code&gt;good first issue&lt;/code&gt;&lt;/a&gt; that I'm willing to put extra time, effort, and resources into helping you through. If you want to tackle one of these issues but feel the need for additional support you can &lt;a href="https://twitter.com/sethmlarson"&gt;message me on Twitter&lt;/a&gt; to ask questions. :-)&lt;/p&gt;

&lt;h2&gt;
  
  
  Ready to Tackle Tougher Issues?
&lt;/h2&gt;

&lt;p&gt;There are a lot of issues to contribute to in this category all marked with the &lt;a href="https://github.com/SethMichaelLarson/mashpack/issues?q=is%3Aissue+is%3Aopen+label%3A%22help+wanted%22"&gt;&lt;code&gt;help wanted&lt;/code&gt; label&lt;/a&gt;. I'll get around to these eventually but if you'd like to contribute and get your name out there this is a great place to start.&lt;/p&gt;

&lt;h2&gt;
  
  
  Are you familiar or interested in learning Cython?
&lt;/h2&gt;

&lt;p&gt;As stated above there is no Cython implementation of the Mashpack Packer or Unpacker objects. Having a Cython implementation would increase the speed of the operations ten-fold and are necessary to be a good competitor to Messagepack. I've marked all &lt;a href="https://github.com/SethMichaelLarson/mashpack/issues?q=is%3Aissue+is%3Aopen+label%3Acython"&gt;issues related to Cython&lt;/a&gt; with a label.&lt;/p&gt;

</description>
      <category>python</category>
      <category>showdev</category>
      <category>messagepack</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Check Name Availability for Multiple Services</title>
      <dc:creator>Seth Michael Larson</dc:creator>
      <pubDate>Fri, 22 Sep 2017 20:02:22 +0000</pubDate>
      <link>https://dev.to/sethmlarson/check-name-availability-for-multiple-services</link>
      <guid>https://dev.to/sethmlarson/check-name-availability-for-multiple-services</guid>
      <description>&lt;p&gt;Hello everyone! This is going to be a little bit different than most of my posts in that it's quite short. I was searching for names for an Open Source project that I have been working on and found it very frustrating that the names I was searching for would be available on one platform and unavailable on another. This led to a lot of time being sunk into searching for a good name which could have been spent programming.&lt;/p&gt;

&lt;p&gt;To aid me in this quest I whipped up a tool called Avail which is run from the command line and outputs whether the name you give it is available on a variety of platforms including GitHub, Dockerhub, npm, Twitter, .com, URL shortener available, and more!&lt;/p&gt;

&lt;p&gt;You can install it via &lt;code&gt;pip&lt;/code&gt; like so:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;python -m pip install avail&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;And then use it like this:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;avail [NAME]&lt;/code&gt; or like this &lt;code&gt;python -m avail [NAME]&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The tool's &lt;a href="https://github.com/SethMichaelLarson/avail"&gt;source code is hosted on GitHub&lt;/a&gt; and is licensed under Apache-2.0. Pull requests and suggestions for more services are welcome!&lt;/p&gt;

&lt;p&gt;I hope this tool is able to help you out, if only to save a few minutes of searching.&lt;/p&gt;

</description>
      <category>showdev</category>
      <category>python</category>
    </item>
  </channel>
</rss>
