Swift API 参数约束

在看Dev Guide的时候,有一些关于请求参数的约束内容,但分布的比较零散。今天早上整理了一下,并做了验证,现在PO出来。

1)account、object、container的自定义元数据约束
  1. 每次请求:自定义的X-Account/Object/Container-Meta-*的条目数量 <= 90 个;
  2. 每次请求:自定义的X-Account/Object/Container-Meta-*的每个value,经过UTF-8的URL-Encoded之后的字节 <= 256(一个汉字算3个字节);
  3. 每次请求:自定义的X-Account/Object/Container-Meta-*的所有<key + value>,经过UTF-8的URL-Encoded之后的字节 <= 4096(一个汉字算3个字节);
  4. X-Account/Object/Container-Meta-*的总量目前没发现限制。虽然官方文档上说,Object和Container的元数据最多是90个,但是经实测发现,只是每次请求最多90个,总量并没有限制。至少,现在还未发现。
          
2)Container名称约束
  1. 不能包含'/'字符:结尾可以添加'/',会被自动忽略,且不计入总长度;
  2. 经过UTF-8的URL-Encoded之后的字节 <= 256。虽然官方文档上说,是经过URL编码之后的长度不能大于256,但经实测发现,这个长度其实是UTF-8字节,URL-Encoded之后,一个汉字(%xx%xx%xx)算3个字节。
 
3)Object名称约束
  1. 不能包含保留字符。官方文档上如是说,但没说哪些是保留字符,源码里也没找到 -。-;
  2. 经过UTF-8的URL-Encoded之后的字节 <= 1024。虽然官方文档上说,是经过URL编码之后的长度不能大于1024,但经实测发现,这个长度其实是UTF-8字节,URL-Encoded之后,一个汉字(%xx%xx%xx)算3个字节。
4)参数(marker、endMarker、prefix)约束
  URL-Encoded的
         
5)Request Line
  Request Line <= 8192字节。 这个完全就是URL-Encoded之后的长度,一个汉字(%xx%xx%xx)算9个字节。
 
以下为源码constraints.py。
约束值的定义:
#: Max file size allowed for objects
MAX_FILE_SIZE = constraints_conf_int('max_file_size', 5368709122)  # 5 * 1024 * 1024 * 1024 + 2
#: Max length of the name of a key for metadata
MAX_META_NAME_LENGTH = constraints_conf_int('max_meta_name_length', 128)
#: Max length of the value of a key for metadata
MAX_META_VALUE_LENGTH = constraints_conf_int('max_meta_value_length', 256)
#: Max number of metadata items
MAX_META_COUNT = constraints_conf_int('max_meta_count', 90)
#: Max overall size of metadata
MAX_META_OVERALL_SIZE = constraints_conf_int('max_meta_overall_size', 4096)
#: Max object name length
MAX_OBJECT_NAME_LENGTH = constraints_conf_int('max_object_name_length', 1024)
#: Max object list length of a get request for a container
CONTAINER_LISTING_LIMIT = constraints_conf_int('container_listing_limit', 10000)
#: Max container list length of a get request for an account
ACCOUNT_LISTING_LIMIT = constraints_conf_int('account_listing_limit', 10000)
#: Max account name length
MAX_ACCOUNT_NAME_LENGTH = constraints_conf_int('max_account_name_length', 256)
#: Max container name length
MAX_CONTAINER_NAME_LENGTH = constraints_conf_int('max_container_name_length', 256)


#: Query string format= values to their corresponding content-type values
FORMAT2CONTENT_TYPE = {'plain': 'text/plain', 'json': 'application/json', 'xml': 'application/xml'}

判断元数据是否合法:

def check_metadata(req, target_type):
    """
    Check metadata sent in the request headers.

    :param req: request object
    :param target_type: str: one of: object, container, or account: indicates
                        which type the target storage for the metadata is
    :raises HTTPBadRequest: bad metadata
    """
    prefix = 'x-%s-meta-' % target_type.lower()
    meta_count = 0
    meta_size = 0
    for key, value in req.headers.iteritems():
        if not key.lower().startswith(prefix):
            continue
        key = key[len(prefix):]
        if not key:
            return HTTPBadRequest(body='Metadata name cannot be empty',
                                  request=req, content_type='text/plain')
        meta_count += 1
        meta_size += len(key) + len(value)
        if len(key) > MAX_META_NAME_LENGTH:
            return HTTPBadRequest(
                body='Metadata name too long; max %d' % MAX_META_NAME_LENGTH,
                request=req, content_type='text/plain')
        elif len(value) > MAX_META_VALUE_LENGTH:
            return HTTPBadRequest(
                body='Metadata value too long; max %d' % MAX_META_VALUE_LENGTH,
                request=req, content_type='text/plain')
        elif meta_count > MAX_META_COUNT:
            return HTTPBadRequest(
                body='Too many metadata items; max %d' % MAX_META_COUNT,
                request=req, content_type='text/plain')
        elif meta_size > MAX_META_OVERALL_SIZE:
            return HTTPBadRequest(
                body='Total metadata too large; max %d'
                % MAX_META_OVERALL_SIZE,
                request=req, content_type='text/plain')
    return None

判断创建对象的数据是否合法:

def check_object_creation(req, object_name):
    """
    Check to ensure that everything is alright about an object to be created.

    :param req: HTTP request object
    :param object_name: name of object to be created
    :raises HTTPRequestEntityTooLarge: the object is too large
    :raises HTTPLengthRequered: missing content-length header and not
                                a chunked request
    :raises HTTPBadRequest: missing or bad content-type header, or
                            bad metadata
    """
    if req.content_length and req.content_length > MAX_FILE_SIZE:
        return HTTPRequestEntityTooLarge(body='Your request is too large.',
                                         request=req,
                                         content_type='text/plain')
    if req.content_length is None and \
            req.headers.get('transfer-encoding') != 'chunked':
        return HTTPLengthRequired(request=req)
    if 'X-Copy-From' in req.headers and req.content_length:
        return HTTPBadRequest(body='Copy requests require a zero byte body',
                              request=req, content_type='text/plain')
    if len(object_name) > MAX_OBJECT_NAME_LENGTH:
        return HTTPBadRequest(body='Object name length of %d longer than %d' %
                              (len(object_name), MAX_OBJECT_NAME_LENGTH),
                              request=req, content_type='text/plain')
    if 'Content-Type' not in req.headers:
        return HTTPBadRequest(request=req, content_type='text/plain',
                              body='No content type')
    if not check_utf8(req.headers['Content-Type']):
        return HTTPBadRequest(request=req, body='Invalid Content-Type',
                              content_type='text/plain')
    if 'x-object-manifest' in req.headers:
        value = req.headers['x-object-manifest']
        container = prefix = None
        try:
            container, prefix = value.split('/', 1)
        except ValueError:
            pass
        if not container or not prefix or '?' in value or '&' in value or \
                prefix[0] == '/':
            return HTTPBadRequest(
                request=req,
                body='X-Object-Manifest must in the format container/prefix')
    return check_metadata(req, 'object')

 

其实我在测试的过程中发现,swift已经相当健壮了,会对违反以上所描述的所有约束的情况进行错误处理:拒绝请求,返回响应的错误码、错误原因说明。因此,我们本打算在SDK层再进行一次约束的想法就没那么重要了,反而会增加复杂度、降低SDK效率。因此,只添加业务层约束就OK,例如:
  1. X-Account/Object/Container-Meta-*中的'*'即key值,ISO-8859-1字符集,即英文字母、-、_、空格等简单字符;
  2. 用户不能直接创建、访问、删除后缀为"_segments"的Container;
  3. 用户不能直接创建、访问、删除后缀为"_versions"的Container。
 
posted @ 2013-03-28 15:45  YUKI小糖  阅读(821)  评论(0编辑  收藏  举报