Skip to content

Instantly share code, notes, and snippets.

@brycied00d
Last active July 16, 2022 14:30
Show Gist options
  • Save brycied00d/0be28e7cd48fd1611e75 to your computer and use it in GitHub Desktop.
Save brycied00d/0be28e7cd48fd1611e75 to your computer and use it in GitHub Desktop.
Moving Speedtest.net's Logic Inside of Nginx
# My goal was two-fold:
# Avoid installing PHP for a single 12-line PHP file (only 5 actual LOC)
# Instead this utilises ngx_lua (and luaJIT) which avoids forking out and keeps the logic in-memory.
# Optimize where I could by moving static resources into the configuration/memory.
# The large download test files can also be moved into a ramdisk to eek out a bit more performance by reducing disk IO
# Serve the same speedtest from multiple domains using only relative URLs and traversing subdomains
# The idea is that speedtest.companyA.com can be served from the same server{} block as speedtest.companyB.com
# The index page has links to allow the user to force a connection over ipv4 or ipv6. A connection to /ipv4, for
# instance, will redirect the user to ipv4.speedtest.$X/ where $X is determined dynamically. Likewise for /ipv6,
# and there's /default to redirect to just speedtest.$X/
# Note that this does assume that "ipv4.speedtest.$X" and "ipv6.speedtest.$X" are setup with A and AAAA
# recordsrespectively.
# From: https://gist.github.com/brycied00d/627ec6c4235405838247
server {
set $ipv4addr "X.X.X.X";
set $ipv6addr "[X:X:X:X::X]";
# Only server, don't care about individual IPs.
listen 80 default_server;
listen [::]:80 default_server ipv6only=on;
root /var/www/html;
index index.html;
# Only server, don't care about hostname
server_name _;
location / {
try_files $uri $uri/ =404;
}
# Embedded index.html for extra awesome.
location = /index.html {
# awk '{$1=$1}{ print }' index.html.static | tr -d '\r\n'
more_set_headers 'Content-Type: text/html';
return 200 '<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"><head><meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" /><title>Mini Bandwidth Speed Test</title></head><body><div style="text-align: center;"><!-- BEGIN SPEED TEST - DO NOT ALTER BELOW--><script type="text/javascript" src="speedtest/swfobject.js?v=2.2"></script><div id="mini-demo">Speedtest.net Mini requires at least version 8 of Flash. Please <a href="http://get.adobe.com/flashplayer/">update your client</a>.</div><!--/mini-demo--><script type="text/javascript">var flashvars = {upload_extension: "nginx"};var params = {wmode: "transparent",quality: "high",menu: "true",allowScriptAccess: "always"};var attributes = {};swfobject.embedSWF("speedtest.swf?v=2.1.8", "mini-demo", "560", "400", "9.0.0", "speedtest/expressInstall.swf", flashvars, params, attributes);</script><!-- END SPEED TEST - DO NOT ALTER ABOVE --></div><p><h1>Other Test Files</h1><a href="/200k">200k</a><br/><a href="/10m">10m</a><br/><a href="/100mb.bin">100mb.bin</a><br/><hr/><a href="/ipv4">Force IPv4</a><br/><a href="/ipv6">Force IPv6</a><br/><a href="/default">Browser IPv4/IPv6 choice</a><br/><hr/>Details on how the above links work can be found in <a href="https://gist.github.com/brycied00d/627ec6c4235405838247">this Gist</a><br/></p><!-- Todo: Automated traceroute back to originating IP? --></body></html>';
}
# Embedded favicon - Just some goofy thing
location = /favicon.ico {
more_set_headers 'Content-Type: image/gif';
expires max;
lua_need_request_body on;
content_by_lua 'ngx.say(ngx.decode_base64("R0lGODlhEAAQAMQAAORHHOVSKudfOulrSOp3WOyDZu6QdvCchPGolfO0o/XBs/fNwfjZ0frl3/zy7////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAkAABAALAAAAAAQABAAAAVVICSOZGlCQAosJ6mu7fiyZeKqNKToQGDsM8hBADgUXoGAiqhSvp5QAnQKGIgUhwFUYLCVDFCrKUE1lBavAViFIDlTImbKC5Gm2hB0SlBCBMQiB0UjIQA7")); ngx.exit(ngx.HTTP_OK);';
}
# Add expiration/cache control headers to all content under /speedtest, and redirect bare access to /
location ~ "^/speedtest" {
expires epoch;
location ~ "/speedtest/?$" {
return 301 /;
}
}
# Embed the upload.php logic in Nginx's configuration directly with ngx_lua
location = /speedtest/upload.nginx {
client_body_buffer_size 1m;
client_body_in_single_buffer on;
lua_need_request_body on;
content_by_lua '
ngx.header.content_type = "text/html; charset=UTF-8";
local args, err = ngx.req.get_post_args()
local size = 500;
for key, val in pairs(args) do
size = size + string.len(key) + string.len(val) + 3;
end
ngx.print("size=", size);
ngx.exit(ngx.HTTP_OK);
';
}
# Embed this pointless and unused file anyways.
location = /speedtest/latency.txt {
more_set_headers 'Content-Type: text/plain';
return 200 "test=test\n";
}
location = /ipv4 {
# IP access requires a hard-coded IPv4 address for the redirect.
if ($host = "[$server_addr]") { return 302 $scheme://$ipv4addr/; }
if ($host = "$server_addr") { return 302 $scheme://$ipv4addr/; }
# A bare "speedtest.____" connection gets ipv4. prepended to the $host
if ($host ~* "^speedtest") { return 302 $scheme://ipv4.$host/; }
# A connection to our ipvX-prepended URL gets that ipvX. replaced with ipv4.
if ($host ~* "^ipv[46].(speedtest\..+)") { return 302 $scheme://ipv4.$1/; }
# Any unhandled patterns get bounced straight back to /
return 302 $scheme://$host/;
}
location = /ipv6 {
# IP access requires a hard-coded IPv6 address for the redirect.
if ($host = $server_addr) { return 302 $scheme://$ipv6addr/; }
# A bare "speedtest.____" connection gets ipv6. prepended to the $host
if ($host ~* "^speedtest") { return 302 $scheme://ipv6.$host/; }
# A connection to our ipvX-prepended URL gets that ipvX. replaced with ipv6.
if ($host ~* "^ipv[46].(speedtest\..+)") { return 302 $scheme://ipv6.$1/; }
# Any unhandled patterns get bounced straight back to /
return 302 $scheme://$host/;
}
location = /default {
#return 200 "Hello default. host=$host scheme=$scheme server_addr=$server_addr\n";
if ($host ~* "^ipv[46].(speedtest\..+)") { return 302 $scheme://$1/; }
# All unhandled patterns, including direct IP and "speedtest." just get bounced back to themselves' /
return 302 $scheme://$host/;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment