2 # Copyright (c) 2011 Greek Research and Technology Network
\r
5 from functools import wraps
\r
7 from time import time
\r
8 from wsgiref.handlers import format_date_time
\r
10 from django.conf import settings
\r
11 from django.http import HttpResponse
\r
13 from pithos.api.faults import Fault, BadRequest, ServiceUnavailable
\r
18 def format_meta_key(k):
\r
20 Convert underscores to dashes and capitalize intra-dash strings.
\r
22 return '-'.join([x.capitalize() for x in k.replace('_', '-').split('-')])
\r
24 def get_meta(request, prefix):
\r
26 Get all prefix-* request headers in a dict. Reformat keys with format_meta_key().
\r
28 prefix = 'HTTP_' + prefix.upper().replace('-', '_')
\r
29 return dict([(format_meta_key(k[5:]), v) for k, v in request.META.iteritems() if k.startswith(prefix)])
\r
31 def get_range(request):
\r
33 Parse a Range header from the request.
\r
34 Either returns None, or an (offset, length) tuple.
\r
35 If no offset is defined offset equals 0.
\r
36 If no length is defined length is None.
\r
39 range = request.GET.get('range')
\r
43 range = range.replace(' ', '')
\r
44 if not range.startswith('bytes='):
\r
47 parts = range.split('-')
\r
51 offset, length = parts
\r
52 if offset == '' and length == '':
\r
57 offset = int(offset)
\r
65 length = int(length)
\r
71 return (offset, length)
\r
73 def update_response_headers(request, response):
\r
74 if request.serialization == 'xml':
\r
75 response['Content-Type'] = 'application/xml; charset=UTF-8'
\r
76 elif request.serialization == 'json':
\r
77 response['Content-Type'] = 'application/json; charset=UTF-8'
\r
79 response['Content-Type'] = 'text/plain; charset=UTF-8'
\r
82 response['Date'] = format_date_time(time())
\r
84 def render_fault(request, fault):
\r
85 response = HttpResponse(status = fault.code)
\r
86 update_response_headers(request, response)
\r
89 def request_serialization(request, format_allowed=False):
\r
91 Return the serialization format requested.
\r
93 Valid formats are 'text' and 'json', 'xml' if `format_allowed` is True.
\r
96 if not format_allowed:
\r
99 format = request.GET.get('format')
\r
100 if format == 'json':
\r
102 elif format == 'xml':
\r
105 for item in request.META.get('HTTP_ACCEPT', '').split(','):
\r
106 accept, sep, rest = item.strip().partition(';')
\r
107 if accept == 'text/plain':
\r
109 elif accept == 'application/json':
\r
111 elif accept == 'application/xml' or accept == 'text/xml':
\r
116 def api_method(http_method = None, format_allowed = False):
\r
118 Decorator function for views that implement an API method.
\r
121 def decorator(func):
\r
123 def wrapper(request, *args, **kwargs):
\r
125 if http_method and request.method != http_method:
\r
126 raise BadRequest('Method not allowed.')
\r
128 # The args variable may contain up to (account, container, object).
\r
129 if len(args) > 1 and len(args[1]) > 256:
\r
130 raise BadRequest('Container name too large.')
\r
131 if len(args) > 2 and len(args[2]) > 1024:
\r
132 raise BadRequest('Object name too large.')
\r
134 # Fill in custom request variables.
\r
135 request.serialization = request_serialization(request, format_allowed)
\r
136 # TODO: Authenticate.
\r
137 request.user = "test"
\r
139 response = func(request, *args, **kwargs)
\r
140 update_response_headers(request, response)
\r
142 except Fault, fault:
\r
143 return render_fault(request, fault)
\r
144 except BaseException, e:
\r
145 logging.exception('Unexpected error: %s' % e)
\r
146 fault = ServiceUnavailable('Unexpected error')
\r
147 return render_fault(request, fault)
\r