Skip to content

Add AVIF image endpoint support #57

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions convert_to_avif.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import os
import subprocess

# Get the path to the images directory
current_dir = os.path.dirname(os.path.abspath(__file__))
images_dir = os.path.join(current_dir, 'httpbin', 'templates', 'images')

# Open the PNG image
png_path = os.path.join(images_dir, 'pig_icon.png')
avif_path = os.path.join(images_dir, 'pig_icon.avif')

# Convert using avifenc command-line tool
try:
subprocess.run(['avifenc', png_path, avif_path, '-y', '420', '-d', '8', '-s', '0'], check=True)
print(f"Successfully converted {png_path} to {avif_path}")
except subprocess.CalledProcessError as e:
print(f"Error converting image: {e}")
except FileNotFoundError:
print("avifenc command-line tool not found. Please install it using: brew install libavif")


32 changes: 24 additions & 8 deletions httpbin/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,9 @@
url_for,
abort,
)
from werkzeug.datastructures import WWWAuthenticate, MultiDict
from werkzeug.datastructures import MultiDict
from werkzeug.http import http_date

try:
from werkzeug.wrappers import Response
except ImportError: # werkzeug < 2.1
Expand All @@ -44,7 +45,6 @@
check_basic_auth,
check_digest_auth,
secure_cookie,
H,
ROBOT_TXT,
ANGRY_ASCII,
parse_authorization_header,
Expand Down Expand Up @@ -228,9 +228,9 @@ def set_cors_headers(response):
if request.method == "OPTIONS":
# Both of these headers are only used for the "preflight request"
# http://www.w3.org/TR/cors/#access-control-allow-methods-response-header
response.headers[
"Access-Control-Allow-Methods"
] = "GET, POST, PUT, DELETE, PATCH, OPTIONS"
response.headers["Access-Control-Allow-Methods"] = (
"GET, POST, PUT, DELETE, PATCH, OPTIONS"
)
response.headers["Access-Control-Max-Age"] = "3600" # 1 hour cache
if request.headers.get("Access-Control-Request-Headers") is not None:
response.headers["Access-Control-Allow-Headers"] = request.headers[
Expand Down Expand Up @@ -349,7 +349,7 @@ def view_headers():
description: The request's headers.
"""

return jsonify(get_dict('headers'))
return jsonify(get_dict("headers"))


@app.route("/user-agent")
Expand Down Expand Up @@ -1721,16 +1721,32 @@ def image_svg():
- image/svg+xml
responses:
200:
description: An SVG image.
description: A SVG image.
"""
data = resource("images/svg_logo.svg")
return Response(data, headers={"Content-Type": "image/svg+xml"})


@app.route("/image/avif")
def image_avif():
"""Returns a simple AVIF image.
---
tags:
- Images
produces:
- image/avif
responses:
200:
description: An AVIF image.
"""
data = resource("images/pig_icon.avif")
return Response(data, headers={"Content-Type": "image/avif"})


def resource(filename):
path = os.path.join(tmpl_dir, filename)
with open(path, "rb") as f:
return f.read()
return f.read()


@app.route("/xml")
Expand Down
20 changes: 8 additions & 12 deletions httpbin/filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ def x_runtime(f, *args, **kwargs):
_t0 = now()
r = f(*args, **kwargs)
_t1 = now()
r.headers['X-Runtime'] = '{0}s'.format(Decimal(str(_t1 - _t0)))
r.headers["X-Runtime"] = "{0}s".format(Decimal(str(_t1 - _t0)))

return r

Expand All @@ -47,20 +47,16 @@ def gzip(f, *args, **kwargs):
content = data

gzip_buffer = BytesIO()
gzip_file = gzip2.GzipFile(
mode='wb',
compresslevel=4,
fileobj=gzip_buffer
)
gzip_file = gzip2.GzipFile(mode="wb", compresslevel=4, fileobj=gzip_buffer)
gzip_file.write(content)
gzip_file.close()

gzip_data = gzip_buffer.getvalue()

if isinstance(data, Response):
data.data = gzip_data
data.headers['Content-Encoding'] = 'gzip'
data.headers['Content-Length'] = str(len(data.data))
data.headers["Content-Encoding"] = "gzip"
data.headers["Content-Length"] = str(len(data.data))

return data

Expand All @@ -84,8 +80,8 @@ def deflate(f, *args, **kwargs):

if isinstance(data, Response):
data.data = deflated_data
data.headers['Content-Encoding'] = 'deflate'
data.headers['Content-Length'] = str(len(data.data))
data.headers["Content-Encoding"] = "deflate"
data.headers["Content-Length"] = str(len(data.data))

return data

Expand All @@ -107,8 +103,8 @@ def brotli(f, *args, **kwargs):

if isinstance(data, Response):
data.data = deflated_data
data.headers['Content-Encoding'] = 'br'
data.headers['Content-Length'] = str(len(data.data))
data.headers["Content-Encoding"] = "br"
data.headers["Content-Length"] = str(len(data.data))

return data

Expand Down
Loading