Skip to content
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

Added @accept() for HTTP Accept: request header support. #15

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
55 changes: 55 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
@@ -1,3 +1,58 @@
This is a modified version of itty.py which attempts to add support for the HTTP 'Accept:' request header.

example code:

::

@get('/')
@accept('text/plain')
def index(request):
return "Hello, World!\n"

@get('/')
@accept('text/xml')
def index(request):
return '<?xml version="1.0"?><greeting>Hello, World!</greeting>\n'

@get('/')
@accept('application/json')
def index(request):
return '{ "greeting": "Hello, World!" }\n'

@get('/')
def index(request):
return "<html><body>Hello, World!</body></html>\n"


output:

::

$ wget -q -O - --header='Accept: text/plain' http://localhost:8080
Hello, World!

$ wget -q -O - --header='Accept: text/xml' http://localhost:8080
<?xml version="1.0"?><greeting>Hello, World!</greeting>

$ wget -q -O - --header='Accept: application/json' http://localhost:8080
{ "greeting": "Hello, World!" }

$ wget -q -O - --header='Accept: text/html' http://localhost:8080
<html><body>Hello, World!</body></html>


This is just a first stab to flesh out the idea.

Limitations (i.e. things which could be improved upon):

* You have to define your methods in order from most specific to least specific.
* Wildcards and 'q' aren't supported yet.

Feel free to take this idea and run with it. Have fun!
Jason Pepas
[email protected]


=======
itty.py
=======
Expand Down
59 changes: 47 additions & 12 deletions itty.py
Original file line number Diff line number Diff line change
Expand Up @@ -312,16 +312,37 @@ def handle_error(exception, request=None):
return not_found(request, exception)


def method_supports_mime_type_in_accept_header(method, accept_string):
"""Determine if the MIME types supported by the method match those in the Accept header."""
if hasattr(method, 'accepted_mime_types') == False:
# if an @accept() decorator hasn't been used, we assume */*
return True

parts = accept_string.split(',')
mime_types_accepted_by_requester = [part.split(';')[0] for part in parts]
for mime_type in mime_types_accepted_by_requester:
if mime_type in method.accepted_mime_types:
return True
return False


def find_matching_url(request):
"""Searches through the methods who've registed themselves with the HTTP decorators."""
if not request.method in REQUEST_MAPPINGS:
raise NotFound("The HTTP request method '%s' is not supported." % request.method)

for url_set in REQUEST_MAPPINGS[request.method]:
match = url_set[0].search(request.path)
for method in REQUEST_MAPPINGS[request.method]:
match = method.re_url.search(request.path)

if match is not None:
return (url_set, match.groupdict())
if match == None:
continue

if hasattr(request, 'HTTP_ACCEPT') == True:
if method_supports_mime_type_in_accept_header(method, request.HTTP_ACCEPT) == False:
continue

url_set = (method.re_url, method.url, method)
return (url_set, match.groupdict())

raise NotFound("Sorry, nothing here.")

Expand Down Expand Up @@ -405,8 +426,9 @@ def get(url):
"""Registers a method as capable of processing GET requests."""
def wrapped(method):
# Register.
re_url = re.compile("^%s$" % add_slash(url))
REQUEST_MAPPINGS['GET'].append((re_url, url, method))
method.url = url
method.re_url = re.compile("^%s$" % add_slash(url))
REQUEST_MAPPINGS['GET'].append(method)
return method
return wrapped

Expand All @@ -415,8 +437,9 @@ def post(url):
"""Registers a method as capable of processing POST requests."""
def wrapped(method):
# Register.
re_url = re.compile("^%s$" % add_slash(url))
REQUEST_MAPPINGS['POST'].append((re_url, url, method))
method.url = url
method.re_url = re.compile("^%s$" % add_slash(url))
REQUEST_MAPPINGS['POST'].append(method)
return method
return wrapped

Expand All @@ -425,8 +448,9 @@ def put(url):
"""Registers a method as capable of processing PUT requests."""
def wrapped(method):
# Register.
re_url = re.compile("^%s$" % add_slash(url))
REQUEST_MAPPINGS['PUT'].append((re_url, url, method))
method.url = url
method.re_url = re.compile("^%s$" % add_slash(url))
REQUEST_MAPPINGS['PUT'].append(method)
new.status = 201
return method
return wrapped
Expand All @@ -436,8 +460,9 @@ def delete(url):
"""Registers a method as capable of processing DELETE requests."""
def wrapped(method):
# Register.
re_url = re.compile("^%s$" % add_slash(url))
REQUEST_MAPPINGS['DELETE'].append((re_url, url, method))
method.url = url
method.re_url = re.compile("^%s$" % add_slash(url))
REQUEST_MAPPINGS['DELETE'].append(method)
return method
return wrapped

Expand All @@ -451,6 +476,16 @@ def wrapped(method):
return wrapped


def accept(mime_type):
"""Specifies which MIME types a method handles."""
def wrapped(method):
if method.__dict__.has_key('accepted_mime_types') is False:
method.accepted_mime_types = []
method.accepted_mime_types.append(mime_type)
return method
return wrapped


# Error handlers

@error(403)
Expand Down