by Tykling
02. nov 2017 14:47 UTC
This is the story of a bug I found in Djangos
Daphne
HTTP and Websocket terminating server, where I had to work around the bug for months while waiting for the fix to make it into a release.
Daphne
is part of the Channels
project which is Djangos
cool websocket thing that recently got adopted as a part of the official Django
project. We've been using Channels
for a while on the the Schedule part of the BornHack website, which is how I found this Daphne
bug to begin with.
FreeBSD
jails
with an nginx
proxy in front. This means that I make heavy use of the X-Forwarded-*
HTTP headers to see the real client IP in my requests. Basically the nginx
proxy receives the request, and does a new request to the backend Django
server to get the response, and then passes that response back to the client. The proxy nginx
adds a couple of headers to the request called X-Forwarded-For
and X-Forwarded-Port
and the backend nginx
and Django
instances can use those headers instead of the client ip (which points to the proxy).
The websocket requests handled by Daphne
were seen with the proxy IP as client IP, not respecting X-Forwarded-For
. This was weird because looking over the code it appeared to have support for those headers, and it turned out to be a bug related to Python2/3 support and encoding of a dict of headers. I fixed the bug back in April and did a pull request which was recently accepted into Daphne. But I needed a solution there and then, I couldn't sit around and wait for the next Daphne
release.
What I needed was a way to get pip
to install the latest Daphne 1.3.0
release (which is based on the 1.3.0
tag on Github
) with my patch on top. I could do this by hand of course, but that is not how I work. I need something I can put into Ansible
and deploy automatically and repeatedly without any fuzz. Fortunately it is easy to make pip
install a package from a Github branch. But first I need to Fork.
So the first thing I did was go to Github
and press Fork
on the Daphne repo. Then I cloned my new fork onto my laptop:
user@dev:~/devel$ git clone git@github.com:tykling/daphne.git Cloning into 'daphne'... remote: Counting objects: 1086, done. remote: Total 1086 (delta 0), reused 0 (delta 0), pack-reused 1086 Receiving objects: 100% (1086/1086), 239.07 KiB | 0 bytes/s, done. Resolving deltas: 100% (711/711), done.
I check that the 1.3.0
tag is present:
user@dev:~/devel/daphne$ git tag | grep 1.3.0 1.3.0
Cool. Then I make a new branch called ws-x-forwarded-for-bugfix
based on the upstream tag 1.3.0
to hold my bugfix:
user@dev:~/devel$ cd daphne/ user@dev:~/devel/daphne$ git checkout -b ws-x-forwarded-for-bugfix 1.3.0 Switched to a new branch 'ws-x-forwarded-for-bugfix' user@dev:~/devel/daphne$
Finally I git cherry-pick
the latest commit from master
and add it to my branch:
user@dev:~/devel/daphne$ git cherry-pick master [ws-x-forwarded-for-bugfix fe7c7df] Make sure headers are always correctly encoded Date: Thu Oct 12 20:06:18 2017 +0200 1 file changed, 10 insertions(+), 5 deletions(-) user@dev:~/devel/daphne$
My bugfix just happened to be the latest commit on the master
branch of the upstream Daphne
repo, otherwise I would have to add some flags to the git cherry-pick
command.
Finally I push the new branch to my fork on Github:
user@dev:~/devel/daphne$ git push --set-upstream origin ws-x-forwarded-for-bugfix Counting objects: 4, done. Delta compression using up to 2 threads. Compressing objects: 100% (4/4), done. Writing objects: 100% (4/4), 654 bytes | 0 bytes/s, done. Total 4 (delta 3), reused 0 (delta 0) remote: Resolving deltas: 100% (3/3), completed with 3 local objects. To github.com:tykling/daphne.git * [new branch] ws-x-forwarded-for-bugfix -> ws-x-forwarded-for-bugfix Branch ws-x-forwarded-for-bugfix set up to track remote branch ws-x-forwarded-for-bugfix from origin. user@dev:~/devel/daphne$
All that remains is to make my deployments use my fork until there is a new release of Daphne
on pip
. This snippet from requirements.txt
shows how I disabled the regular Daphne
and made it use my fork from Github
:
#temp fix for https://github.com/django/daphne/pull/140 #daphne==1.3.0 git+https://github.com/tykling/daphne@ws-x-forwarded-for-bugfix
Thats all. It works and will keep things running until a new Daphne
release hits the streets. Until then!