-
-
Save tipabu/e1a03a8486c2fa57b4bc to your computer and use it in GitHub Desktop.
Make Swift (or any WSGI app) less reliable
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
''' | |
Sometimes, Swift is a little *too* reliable; let's fix that. | |
With this middleware, operators can throw a bit of chaos at | |
their users. This may be achieved in two ways: | |
* Use max_file_size to limit the size of responses. This | |
doesn't affect any headers (so Content-Length is | |
unmodified). This can be used to check that clients resume | |
interrupted downloads. If zero (the default), there is no | |
limit (ie, this is disabled). | |
* Use failure_chance to randomly return empty responses | |
(with status line defined by failure_reason) instead of | |
passing the request further down the pipeline. This should | |
be a float in the range [0, 1], with 0 (the default) | |
meaning "always forward the request" and 1 meaning "always | |
fail prematurely". | |
If you are using a non-zero failure_chance, you probably want | |
this before (to the left of) SLO in your pipeline. | |
''' | |
import random | |
def capped_iter(app_iter, max_len): | |
''' | |
Wrap a byte iterator to limit how many bytes it will yield | |
:param app_iter: the byte iterator to wrap | |
:param max_len: the maximum number of bytes to yield | |
''' | |
def exploding_iter(): | |
'''A generator to do the wrapping/exploding.''' | |
for chunk in app_iter: | |
yield chunk[:exploding_iter.to_send] | |
exploding_iter.to_send -= len(chunk) | |
if exploding_iter.to_send < 0: | |
raise Exception('Kaboom!') | |
exploding_iter.to_send = max_len | |
return exploding_iter() | |
def filter_factory(global_conf, **local_conf): | |
max_file_size = max(int(local_conf.get('max_file_size', '0')), 0) | |
failure_chance = min(max(float(local_conf.get('failure_chance', '0.0')), | |
0.0), 1.0) | |
failure_reason = local_conf.get('failure_reason', '520 Unknown Error') | |
def filt(app): | |
def handler(env, start_response): | |
if random.random() < failure_chance: | |
start_response(failure_reason, [('Content-Length', '0')]) | |
return [''] | |
app_iter = app(env, start_response) | |
if max_file_size > 0: | |
return capped_iter(app_iter, max_file_size) | |
return app_iter | |
return handler | |
return filt |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment