← back to Paul Conrad Archive
tests/test_image_rail_content.py
54 lines
"""Copyright rail, layer 2: images/scans served from EXTENSIONLESS URLs are refused on
Content-Type or magic bytes before the body is read, and nothing is cached."""
import threading
from http.server import BaseHTTPRequestHandler, HTTPServer
import pytest
from conrad.crawlers import base
JPEG = b"\xff\xd8\xff\xe0" + b"\x00" * 64
ROUTES = {
"/picture": ("image/jpeg", JPEG), # honest image type, no extension
"/disguised": ("text/html", JPEG), # lies about its type
"/scan": ("application/pdf", b"%PDF-1.4 x"), # newspaper scan
"/page": ("text/html; charset=utf-8", b"<html>Conrad cartoon caption</html>"),
}
class H(BaseHTTPRequestHandler):
def do_GET(self):
ctype, body = ROUTES[self.path]
self.send_response(200)
self.send_header("Content-Type", ctype)
self.send_header("Content-Length", str(len(body)))
self.end_headers()
self.wfile.write(body)
def log_message(self, *a):
pass
@pytest.fixture()
def server(monkeypatch, tmp_path):
monkeypatch.setattr(base.config, "REQUEST_DELAY", 0)
monkeypatch.setattr(base.config, "CACHE_DIR", tmp_path)
srv = HTTPServer(("127.0.0.1", 0), H)
threading.Thread(target=srv.serve_forever, daemon=True).start()
yield f"http://127.0.0.1:{srv.server_port}", tmp_path
srv.shutdown()
@pytest.mark.parametrize("path", ["/picture", "/disguised", "/scan"])
def test_binary_refused_and_not_cached(server, path):
url, cache = server
with pytest.raises(base.Blocked, match="copyright rail"):
base.Http().get(url + path, check_robots=False)
assert not any((cache / "http").iterdir()), "refused response must never reach the cache"
def test_html_still_fetched(server):
url, _ = server
status, body = base.Http().get(url + "/page", check_robots=False)
assert status == 200 and "Conrad cartoon caption" in body