def flow_to_exchange(flow): req = construct_request(flow) resp = construct_response(flow) exch = httpolice.Exchange(req, [resp] if resp else []) exch.silence([int(id_) for id_ in ctx.options.httpolice_silence]) httpolice.check_exchange(exch) return exch
def test_prefer(): [exch1] = load_from_file('funny_prefer') assert exch1.request.headers.prefer.value == [ Parametrized(Parametrized(prefer.handling, u'lenient'), [ Parametrized(u'param1', u"this is a parameter to 'handling'!"), Parametrized(u'param2', None), ]), Unavailable(b'BWS-is-not-parsed = because-see-errata'), Parametrized(Parametrized(prefer.wait, 600), []), Parametrized(Parametrized(u'my-pref', None), [ None, None, Parametrized(u'foo', None), None, None, Parametrized(u'bar', None), ]), Parametrized(Parametrized(prefer.respond_async, None), []), Parametrized(Parametrized(prefer.wait, 0), []), Parametrized( Parametrized(prefer.return_, Unavailable(b'something-else')), []), ] assert exch1.request.headers.prefer.wait == 600 assert exch1.request.headers.prefer.respond_async assert isinstance(exch1.request.headers.prefer.return_, Unavailable) assert exch1.request.headers.prefer[u'quux'] is None assert isinstance( exch1.responses[0].headers.preference_applied.respond_async, Unavailable) check_exchange(exch1) buf = io.BytesIO() text_report([exch1], buf) assert b'Preference-Applied: respond-async=true was not requested' \ in buf.getvalue() # not "respond-async=Unavailable"
def test_informational_response_after_final(): exch = Exchange( Request( u'https', u'POST', u'/process/', u'HTTP/1.1', [ (u'Host', b'example.com'), (u'User-Agent', b'demo'), (u'Content-Length', b'14'), (u'Content-Type', b'text/plain'), (u'Expect', b'100-continue'), ], b'Hello world!\r\n', ), [ Response(u'HTTP/1.1', 100, u'Continue', [], b''), Response( u'HTTP/1.1', 204, u'No Content', [(u'Date', b'Fri, 02 Feb 2018 15:44:33 GMT')], b'', ), Response(u'HTTP/1.1', 102, u'Processing', [], b''), ], ) check_exchange(exch) assert [notice.id for notice in exch.request.notices] == [] assert [notice.id for notice in exch.responses[0].notices] == [] assert [notice.id for notice in exch.responses[1].notices] == [] assert [notice.id for notice in exch.responses[2].notices] == [1304]
def test_informational_response_after_final(): exch = Exchange( Request( u'https', u'POST', u'/process/', u'HTTP/1.1', [ (u'Host', b'example.com'), (u'User-Agent', b'demo'), (u'Content-Length', b'14'), (u'Content-Type', b'text/plain'), (u'Expect', b'100-continue'), ], b'Hello world!\r\n', ), [ Response( u'HTTP/1.1', 100, u'Continue', [], b'' ), Response( u'HTTP/1.1', 204, u'No Content', [(u'Date', b'Fri, 02 Feb 2018 15:44:33 GMT')], b'', ), Response( u'HTTP/1.1', 102, u'Processing', [], b'' ), ], ) check_exchange(exch) assert [notice.id for notice in exch.request.notices] == [] assert [notice.id for notice in exch.responses[0].notices] == [] assert [notice.id for notice in exch.responses[1].notices] == [] assert [notice.id for notice in exch.responses[2].notices] == [1304]
def response(context, flow): req = construct_request(flow) resp = construct_response(flow) exch = httpolice.Exchange(req, [resp]) if context.args.silence: exch.silence(context.args.silence) httpolice.check_exchange(exch) context.exchanges.append(exch)
def test_fuzz(i): orig_state = random.getstate() random.seed(123456789 + i) # Some arbitrary, but deterministic number. exch = make_exchange() random.setstate(orig_state) check_exchange(exch) text_report([exch], io.BytesIO()) html_report([exch], io.BytesIO())
def har_input(data): creator = data['log']['creator']['name'] bad_exchanges = [] exchanges = [ _process_entry(entry, creator, 'from chrome') for entry in data['log']['entries'] ] # check for exch in exchanges: httpolice.check_exchange(exch) if any(notice.severity > httpolice.Severity.debug for msg in [exch.request] + exch.responses for notice in msg.notices): bad_exchanges.append(exch) # report out = StringIO.StringIO() if bad_exchanges: httpolice.html_report(bad_exchanges, out) return out
def test_fuzz(i, state=None): if state is None: state = random.getstate() else: random.setstate(state) req = random.choice([ None, Request(random.choice(schemes), random.choice(methods), make_request_target(), random.choice(versions), make_headers(max_num=5), make_body(), make_headers(max_num=2)) ]) resps = [ Response(random.choice(versions), make_status_code(), make_reason_phrase(), make_headers(max_num=5), make_body(), make_headers(max_num=2)) for _ in range(random.randint(0, 2)) ] try: exch = Exchange(req, resps) check_exchange(exch) text_report([exch], six.BytesIO()) html_report([exch], six.BytesIO()) except Exception: filename = 'fuzz-state-%02d.pickle' % i with io.open(filename, 'wb') as f: pickle.dump(state, f) raise
def process_response(self, request, response): if not get_setting('ENABLE'): return response # Importing `httpolice` can execute a lot of code, # so we only do it when it's really time for action. import httpolice req_method = _force_text(request.method) req_headers = httpolice.helpers.headers_from_cgi(request.META) req_target = _force_text(request.path) if request.META.get('QUERY_STRING'): req_target += u'?' + _force_text(request.META['QUERY_STRING']) try: # This can raise `django.http.request.RawPostDataException`, saying # "You cannot access body after reading from request's data stream" req_body = request.body except Exception: # ...but `RawPostDataException` is not documented in Django API, # so catch everything. req_body = None # A ``Content-Type`` of ``text/plain`` is automatically added # to requests that have none (such as GET requests). if req_body == b'' and req_method in [u'GET', u'HEAD', u'DELETE']: req_headers = [entry for entry in req_headers if entry != (u'Content-Type', b'text/plain')] req = httpolice.Request( scheme=_force_text(request.scheme), method=req_method, target=req_target, version=None, header_entries=req_headers, body=req_body, ) if req_method == u'HEAD': # Body is automatically stripped from responses to HEAD, # but not at this point in the response lifecycle. resp_body = b'' elif response.streaming: resp_body = None # Unknown. else: resp_body = response.content resp = httpolice.Response( version=None, status=response.status_code, reason=_force_text(response.reason_phrase), header_entries=[ (_force_text(name), value) for (name, value) in response.items()], body=resp_body, ) exchange = httpolice.Exchange(req, [resp]) exchange.silence(get_setting('SILENCE')) httpolice.check_exchange(exchange) backlog.appendleft(exchange) if get_setting('RAISE') and any(notice.severity == httpolice.ERROR for notice in resp.notices): raise ProtocolError(exchange) return response
import io import httpolice exchanges = [ httpolice.Exchange( httpolice.Request(u'https', u'GET', u'/index.html', u'HTTP/1.1', [(u'Host', b'example.com')], b''), [ httpolice.Response(u'HTTP/1.1', 401, u'Unauthorized', [(u'Content-Type', b'text/plain')], b'No way!'), ]) ] bad_exchanges = [] for exch in exchanges: exch.silence([1089, 1227]) # Errors we don't care about httpolice.check_exchange(exch) if any(notice.severity > httpolice.Severity.comment for resp in exch.responses # We only care about responses for notice in resp.notices): bad_exchanges.append(exch) if bad_exchanges: with io.open('report.html', 'wb') as f: httpolice.html_report(bad_exchanges, f) print('%d exchanges had problems; report written to file' % len(bad_exchanges))
# ``conf.py`` "can execute arbitrarily complex code", so maybe it's OK. # See also: https://stackoverflow.com/q/38547509/200445 if os.path.exists('_extra'): shutil.rmtree('_extra') os.mkdir('_extra') with io.open('_extra/notices.html', 'wb') as notices_file: httpolice.reports.html.list_notices(notices_file) with io.open('_extra/showcase.html', 'wb') as showcase_file: exchanges = list(httpolice.inputs.combined_input( ['../test/combined_data/showcase.https'])) for exch in exchanges: httpolice.check_exchange(exch) httpolice.html_report(exchanges, showcase_file) # -- General configuration ------------------------------------------------ # If your documentation needs a minimal Sphinx version, state it here. #needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = [ 'sphinx.ext.autodoc', ]