Revision bd40abfa snf-pithos-app/pithos/api/util.py
b/snf-pithos-app/pithos/api/util.py | ||
---|---|---|
50 | 50 |
|
51 | 51 |
from synnefo.lib.parsedate import parse_http_date_safe, parse_http_date |
52 | 52 |
from synnefo.lib.astakos import get_user |
53 |
from snf_django.lib.api import faults |
|
53 | 54 |
|
54 |
from pithos.api.faults import ( |
|
55 |
Fault, NotModified, BadRequest, Unauthorized, Forbidden, ItemNotFound, |
|
56 |
Conflict, LengthRequired, PreconditionFailed, RequestEntityTooLarge, |
|
57 |
RangeNotSatisfiable, InternalServerError, NotImplemented) |
|
58 | 55 |
from pithos.api.settings import (BACKEND_DB_MODULE, BACKEND_DB_CONNECTION, |
59 | 56 |
BACKEND_BLOCK_MODULE, BACKEND_BLOCK_PATH, |
60 | 57 |
BACKEND_BLOCK_UMASK, |
... | ... | |
143 | 140 |
|
144 | 141 |
def check_meta_headers(meta): |
145 | 142 |
if len(meta) > 90: |
146 |
raise BadRequest('Too many headers.') |
|
143 |
raise faults.BadRequest('Too many headers.')
|
|
147 | 144 |
for k, v in meta.iteritems(): |
148 | 145 |
if len(k) > 128: |
149 |
raise BadRequest('Header name too large.') |
|
146 |
raise faults.BadRequest('Header name too large.')
|
|
150 | 147 |
if len(v) > 256: |
151 |
raise BadRequest('Header value too large.') |
|
148 |
raise faults.BadRequest('Header value too large.')
|
|
152 | 149 |
|
153 | 150 |
|
154 | 151 |
def get_account_headers(request): |
... | ... | |
158 | 155 |
for k, v in get_header_prefix(request, 'X-Account-Group-').iteritems(): |
159 | 156 |
n = k[16:].lower() |
160 | 157 |
if '-' in n or '_' in n: |
161 |
raise BadRequest('Bad characters in group name') |
|
158 |
raise faults.BadRequest('Bad characters in group name')
|
|
162 | 159 |
groups[n] = v.replace(' ', '').split(',') |
163 | 160 |
while '' in groups[n]: |
164 | 161 |
groups[n].remove('') |
... | ... | |
401 | 398 |
if if_modified_since is not None: |
402 | 399 |
if_modified_since = parse_http_date_safe(if_modified_since) |
403 | 400 |
if if_modified_since is not None and int(meta['modified']) <= if_modified_since: |
404 |
raise NotModified('Resource has not been modified') |
|
401 |
raise faults.NotModified('Resource has not been modified')
|
|
405 | 402 |
|
406 | 403 |
if_unmodified_since = request.META.get('HTTP_IF_UNMODIFIED_SINCE') |
407 | 404 |
if if_unmodified_since is not None: |
408 | 405 |
if_unmodified_since = parse_http_date_safe(if_unmodified_since) |
409 | 406 |
if if_unmodified_since is not None and int(meta['modified']) > if_unmodified_since: |
410 |
raise PreconditionFailed('Resource has been modified') |
|
407 |
raise faults.PreconditionFailed('Resource has been modified')
|
|
411 | 408 |
|
412 | 409 |
|
413 | 410 |
def validate_matching_preconditions(request, meta): |
... | ... | |
420 | 417 |
if_match = request.META.get('HTTP_IF_MATCH') |
421 | 418 |
if if_match is not None: |
422 | 419 |
if etag is None: |
423 |
raise PreconditionFailed('Resource does not exist') |
|
420 |
raise faults.PreconditionFailed('Resource does not exist')
|
|
424 | 421 |
if if_match != '*' and etag not in [x.lower() for x in parse_etags(if_match)]: |
425 |
raise PreconditionFailed('Resource ETag does not match') |
|
422 |
raise faults.PreconditionFailed('Resource ETag does not match')
|
|
426 | 423 |
|
427 | 424 |
if_none_match = request.META.get('HTTP_IF_NONE_MATCH') |
428 | 425 |
if if_none_match is not None: |
... | ... | |
431 | 428 |
if if_none_match == '*' or etag in [x.lower() for x in parse_etags(if_none_match)]: |
432 | 429 |
# TODO: Continue if an If-Modified-Since header is present. |
433 | 430 |
if request.method in ('HEAD', 'GET'): |
434 |
raise NotModified('Resource ETag matches') |
|
435 |
raise PreconditionFailed('Resource exists or ETag matches') |
|
431 |
raise faults.NotModified('Resource ETag matches')
|
|
432 |
raise faults.PreconditionFailed('Resource exists or ETag matches')
|
|
436 | 433 |
|
437 | 434 |
|
438 | 435 |
def split_container_object_string(s): |
... | ... | |
464 | 461 |
dest_account, dest_container, dest_name, |
465 | 462 |
content_type, 'pithos', meta, False, permissions, src_version, delimiter) |
466 | 463 |
except NotAllowedError: |
467 |
raise Forbidden('Not allowed') |
|
464 |
raise faults.Forbidden('Not allowed')
|
|
468 | 465 |
except (ItemNotExists, VersionNotExists): |
469 |
raise ItemNotFound('Container or object does not exist') |
|
466 |
raise faults.ItemNotFound('Container or object does not exist')
|
|
470 | 467 |
except ValueError: |
471 |
raise BadRequest('Invalid sharing header') |
|
468 |
raise faults.BadRequest('Invalid sharing header')
|
|
472 | 469 |
except QuotaError, e: |
473 |
raise RequestEntityTooLarge('Quota error: %s' % e) |
|
470 |
raise faults.RequestEntityTooLarge('Quota error: %s' % e)
|
|
474 | 471 |
if public is not None: |
475 | 472 |
try: |
476 | 473 |
request.backend.update_object_public(request.user_uniq, dest_account, dest_container, dest_name, public) |
477 | 474 |
except NotAllowedError: |
478 |
raise Forbidden('Not allowed') |
|
475 |
raise faults.Forbidden('Not allowed')
|
|
479 | 476 |
except ItemNotExists: |
480 |
raise ItemNotFound('Object does not exist') |
|
477 |
raise faults.ItemNotFound('Object does not exist')
|
|
481 | 478 |
return version_id |
482 | 479 |
|
483 | 480 |
|
... | ... | |
495 | 492 |
def get_content_length(request): |
496 | 493 |
content_length = get_int_parameter(request.META.get('CONTENT_LENGTH')) |
497 | 494 |
if content_length is None: |
498 |
raise LengthRequired('Missing or invalid Content-Length header') |
|
495 |
raise faults.LengthRequired('Missing or invalid Content-Length header')
|
|
499 | 496 |
return content_length |
500 | 497 |
|
501 | 498 |
|
... | ... | |
604 | 601 |
if '*' in ret['read']: |
605 | 602 |
ret['read'] = ['*'] |
606 | 603 |
if len(ret['read']) == 0: |
607 |
raise BadRequest( |
|
604 |
raise faults.BadRequest(
|
|
608 | 605 |
'Bad X-Object-Sharing header value: invalid length') |
609 | 606 |
elif perm.startswith('write='): |
610 | 607 |
ret['write'] = list(set( |
... | ... | |
614 | 611 |
if '*' in ret['write']: |
615 | 612 |
ret['write'] = ['*'] |
616 | 613 |
if len(ret['write']) == 0: |
617 |
raise BadRequest( |
|
614 |
raise faults.BadRequest(
|
|
618 | 615 |
'Bad X-Object-Sharing header value: invalid length') |
619 | 616 |
else: |
620 |
raise BadRequest( |
|
617 |
raise faults.BadRequest(
|
|
621 | 618 |
'Bad X-Object-Sharing header value: missing prefix') |
622 | 619 |
|
623 | 620 |
# replace displayname with uuid |
... | ... | |
630 | 627 |
getattr(request, 'token', None), x) \ |
631 | 628 |
for x in ret.get('write', [])] |
632 | 629 |
except ItemNotExists, e: |
633 |
raise BadRequest( |
|
630 |
raise faults.BadRequest(
|
|
634 | 631 |
'Bad X-Object-Sharing header value: unknown account: %s' % e) |
635 | 632 |
|
636 | 633 |
# Keep duplicates only in write list. |
... | ... | |
660 | 657 |
return True |
661 | 658 |
elif public == 'false' or public == '': |
662 | 659 |
return False |
663 |
raise BadRequest('Bad X-Object-Public header value') |
|
660 |
raise faults.BadRequest('Bad X-Object-Public header value')
|
|
664 | 661 |
|
665 | 662 |
|
666 | 663 |
def raw_input_socket(request): |
... | ... | |
692 | 689 |
if data == '': |
693 | 690 |
return |
694 | 691 |
yield data |
695 |
raise BadRequest('Maximum size is reached') |
|
692 |
raise faults.BadRequest('Maximum size is reached')
|
|
696 | 693 |
|
697 | 694 |
# Long version (do the dechunking). |
698 | 695 |
data = '' |
... | ... | |
710 | 707 |
chunk_length = chunk_length[:pos] |
711 | 708 |
try: |
712 | 709 |
chunk_length = int(chunk_length, 16) |
713 |
except Exception, e:
|
|
714 |
raise BadRequest('Bad chunk size') |
|
710 |
except Exception: |
|
711 |
raise faults.BadRequest('Bad chunk size')
|
|
715 | 712 |
# TODO: Change to something more appropriate. |
716 | 713 |
# Check if done. |
717 | 714 |
if chunk_length == 0: |
... | ... | |
730 | 727 |
data = data[blocksize:] |
731 | 728 |
yield ret |
732 | 729 |
sock.read(2) # CRLF |
733 |
raise BadRequest('Maximum size is reached') |
|
730 |
raise faults.BadRequest('Maximum size is reached')
|
|
734 | 731 |
else: |
735 | 732 |
if length > MAX_UPLOAD_SIZE: |
736 |
raise BadRequest('Maximum size is reached') |
|
733 |
raise faults.BadRequest('Maximum size is reached')
|
|
737 | 734 |
while length > 0: |
738 | 735 |
data = sock.read(min(length, blocksize)) |
739 | 736 |
if not data: |
740 |
raise BadRequest() |
|
737 |
raise faults.BadRequest()
|
|
741 | 738 |
length -= len(data) |
742 | 739 |
yield data |
743 | 740 |
|
... | ... | |
820 | 817 |
try: |
821 | 818 |
self.block = self.backend.get_block(self.block_hash) |
822 | 819 |
except ItemNotExists: |
823 |
raise ItemNotFound('Block does not exist') |
|
820 |
raise faults.ItemNotFound('Block does not exist')
|
|
824 | 821 |
|
825 | 822 |
# Get the data from the block. |
826 | 823 |
bo = self.offset % self.backend.block_size |
... | ... | |
884 | 881 |
offset < 0 or offset >= size or |
885 | 882 |
offset + length > size] |
886 | 883 |
if len(check) > 0: |
887 |
raise RangeNotSatisfiable('Requested range exceeds object limits') |
|
884 |
raise faults.RangeNotSatisfiable('Requested range exceeds object limits')
|
|
888 | 885 |
ret = 206 |
889 | 886 |
if_range = request.META.get('HTTP_IF_RANGE') |
890 | 887 |
if if_range: |
... | ... | |
1005 | 1002 |
k.decode('ascii') |
1006 | 1003 |
v.decode('ascii') |
1007 | 1004 |
except UnicodeDecodeError: |
1008 |
raise BadRequest('Bad character in headers.') |
|
1005 |
raise faults.BadRequest('Bad character in headers.')
|
|
1009 | 1006 |
if '%' in k or '%' in v: |
1010 | 1007 |
del(request.META[k]) |
1011 | 1008 |
request.META[unquote(k)] = smart_unicode(unquote( |
... | ... | |
1035 | 1032 |
|
1036 | 1033 |
|
1037 | 1034 |
def render_fault(request, fault): |
1038 |
if isinstance(fault, InternalServerError) and settings.DEBUG: |
|
1035 |
if isinstance(fault, faults.InternalServerError) and settings.DEBUG:
|
|
1039 | 1036 |
fault.details = format_exc(fault) |
1040 | 1037 |
|
1041 | 1038 |
request.serialization = 'text' |
... | ... | |
1085 | 1082 |
def wrapper(request, *args, **kwargs): |
1086 | 1083 |
try: |
1087 | 1084 |
if http_method and request.method != http_method: |
1088 |
raise BadRequest('Method not allowed.') |
|
1085 |
raise faults.BadRequest('Method not allowed.')
|
|
1089 | 1086 |
|
1090 | 1087 |
if user_required: |
1091 | 1088 |
token = None |
... | ... | |
1099 | 1096 |
token, |
1100 | 1097 |
request_usage) |
1101 | 1098 |
if getattr(request, 'user', None) is None: |
1102 |
raise Unauthorized('Access denied') |
|
1099 |
raise faults.Unauthorized('Access denied')
|
|
1103 | 1100 |
assert getattr(request, 'user_uniq', None) != None |
1104 | 1101 |
request.user_usage = get_pithos_usage(request.user.get('usage', [])) |
1105 | 1102 |
request.token = request.GET.get('X-Auth-Token', request.META.get('HTTP_X_AUTH_TOKEN', token)) |
1106 | 1103 |
|
1107 | 1104 |
# The args variable may contain up to (account, container, object). |
1108 | 1105 |
if len(args) > 1 and len(args[1]) > 256: |
1109 |
raise BadRequest('Container name too large.') |
|
1106 |
raise faults.BadRequest('Container name too large.')
|
|
1110 | 1107 |
if len(args) > 2 and len(args[2]) > 1024: |
1111 |
raise BadRequest('Object name too large.') |
|
1108 |
raise faults.BadRequest('Object name too large.')
|
|
1112 | 1109 |
|
1113 | 1110 |
# Format and check headers. |
1114 | 1111 |
update_request_headers(request) |
... | ... | |
1121 | 1118 |
response = func(request, *args, **kwargs) |
1122 | 1119 |
update_response_headers(request, response) |
1123 | 1120 |
return response |
1124 |
except Fault, fault: |
|
1121 |
except faults.Fault, fault:
|
|
1125 | 1122 |
if fault.code >= 500: |
1126 | 1123 |
logger.exception("API Fault") |
1127 | 1124 |
return render_fault(request, fault) |
1128 | 1125 |
except BaseException, e: |
1129 | 1126 |
logger.exception('Unexpected error: %s' % e) |
1130 |
fault = InternalServerError('Unexpected error') |
|
1127 |
fault = faults.InternalServerError('Unexpected error')
|
|
1131 | 1128 |
return render_fault(request, fault) |
1132 | 1129 |
finally: |
1133 | 1130 |
if getattr(request, 'backend', None) is not None: |
Also available in: Unified diff