blob: d9650e6d02f4656202130669a909bf9b87ddac98 [file] [log] [blame]
# -*- coding: utf-8 -*-
"""
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
"""
"""
把Python的数据结构根据Hessian协议序列化为相应的字节数组
当前支持的数据类型:
* bool
* int
* long
* float
* double
* java.lang.String
* java.lang.Object
"""
import struct
from dubbo.common.constants import MIN_INT_32, MAX_INT_32, DEFAULT_REQUEST_META
from dubbo.common.exceptions import HessianTypeError
from dubbo.common.util import double_to_long_bits, get_invoke_id
class Object(object):
"""
创建一个Java对象
"""
def __init__(self, path, values=None):
"""
:param path: Java对象的路径,例如:java.lang.Object
:param values: 可以在创建对象时就进行赋值
"""
if not isinstance(path, (str, unicode)):
raise ValueError('Object path {} should be string type.'.format(path))
self.__path = path
if not isinstance(values, dict):
values = {}
self.__values = values
def __getitem__(self, key):
return self.__values[key]
def __setitem__(self, key, value):
if not isinstance(key, (str, unicode)):
raise ValueError('Object key {} should be string type.'.format(key))
self.__values[key] = value
def __delitem__(self, key):
del self.__values[key]
def __repr__(self):
return '<java object {} at {} with {}>'.format(self.__path, hex(id(self)), self.__values)
def __contains__(self, key):
return key in self.__values
def keys(self):
return self.__values.keys()
def get_path(self):
return self.__path
class Request(object):
"""
A class for dumping dubbo request body.
All types can be dumped:
* boolean
* int
* long
* double
* string
* object
"""
def __init__(self, request):
self.__body = request
self.__classes = []
self.types = [] # 泛型
self.invoke_id = get_invoke_id()
def encode(self):
"""
把请求序列化为字节数组
:return:
"""
request_body = self._encode_request_body()
invoke_id = list(bytearray(struct.pack('!q', self.invoke_id)))
request_head = DEFAULT_REQUEST_META + invoke_id + get_request_body_length(request_body)
return bytearray(request_head + request_body)
def _get_parameter_types(self, arguments):
"""
针对所有的参数计算得到参数类型字符串
:param arguments:
:return:
"""
parameter_types = ''
# 判断并得出参数的类型
for argument in arguments:
parameter_types += self._get_class_name(argument)
return parameter_types
def _get_class_name(self, _class):
"""
根据一个字段的类型得到其在Java中对应类的全限定名
转换规则:https://stackoverflow.com/a/3442100/4614538
:param _class:
:return:
"""
if isinstance(_class, bool): # bool类型的判断必须放在int类型判断的前面
return 'Z'
elif isinstance(_class, int):
if MIN_INT_32 <= _class <= MAX_INT_32:
return 'I'
else:
return 'J'
elif isinstance(_class, float):
return 'D'
elif isinstance(_class, (str, unicode)):
return 'L' + 'java/lang/String' + ';'
elif isinstance(_class, Object):
path = _class.get_path()
path = 'L' + path.replace('.', '/') + ';'
return path
elif isinstance(_class, list):
if len(_class) == 0:
raise HessianTypeError('Method parameter {} is a list but length is zero'.format(_class))
return '[' + self._get_class_name(_class[0])
else:
raise HessianTypeError('Unknown argument type: {0}'.format(_class))
def _encode_request_body(self):
"""
对所有的已知的参数根据dubbo协议进行编码
:return:
"""
dubbo_version = self.__body['dubbo_version']
path = self.__body['path']
version = self.__body['version']
method = self.__body['method']
arguments = self.__body['arguments']
body = []
body.extend(self._encode_single_value(dubbo_version))
body.extend(self._encode_single_value(path))
body.extend(self._encode_single_value(version))
body.extend(self._encode_single_value(method))
body.extend(self._encode_single_value(self._get_parameter_types(arguments)))
for argument in arguments:
body.extend(self._encode_single_value(argument))
attachments = {
'path': path,
'interface': path,
'version': version
}
# attachments参数以H开头,以Z结尾
body.append(ord('H'))
for key in attachments.keys():
value = attachments[key]
body.extend(self._encode_single_value(key))
body.extend(self._encode_single_value(value))
body.append(ord('Z'))
# 因为在上面的逻辑中没有对byte大小进行检测,所以在这里进行统一的处理
for i in xrange(len(body)):
body[i] = body[i] & 0xff
return body
@staticmethod
def _encode_bool(value):
"""
对bool类型进行编码
:param value:
:return:
"""
result = []
if value:
result.append(ord('T'))
else:
result.append(ord('F'))
return result
@staticmethod
def _encode_int(value):
"""
对整数进行编码
:param value:
:return:
"""
result = []
# 超出int类型范围的值则转化为long类型
# 这里问题在于对于落在int范围内的数字,我们无法判断其是long类型还是int类型,所以一律认为其是int类型
if value > MAX_INT_32 or value < MIN_INT_32:
result.append(ord('L'))
result.extend(list(bytearray(struct.pack('!q', value))))
return result
if -0x10 <= value <= 0x2f:
result.append(value + 0x90)
elif -0x800 <= value <= 0x7ff:
result.append(0xc8 + (value >> 8))
result.append(value)
elif -0x40000 <= value <= 0x3ffff:
result.append(0xd4 + (value >> 16))
result.append(value >> 8)
result.append(value)
else:
result.append(ord('I'))
result.append(value >> 24)
result.append(value >> 16)
result.append(value >> 8)
result.append(value)
return result
@staticmethod
def _encode_float(value):
"""
对浮点类型进行编码
:param value:
:return:
"""
result = []
int_value = int(value)
if int_value == value:
if int_value == 0:
result.append(0x5b)
return result
elif int_value == 1:
result.append(0x5c)
return result
elif -0x80 <= int_value < 0x80:
result.append(0x5d)
result.append(int_value)
return result
elif -0x8000 <= int_value < 0x8000:
result.append(0x5e)
result.append(int_value >> 8)
result.append(int_value)
return result
mills = int(value * 1000)
if 0.001 * mills == value and MIN_INT_32 <= mills <= MAX_INT_32:
result.append(0x5f)
result.append(mills >> 24)
result.append(mills >> 16)
result.append(mills >> 8)
result.append(mills)
return result
bits = double_to_long_bits(value)
result.append(ord('D'))
result.append(bits >> 56)
result.append(bits >> 48)
result.append(bits >> 40)
result.append(bits >> 32)
result.append(bits >> 24)
result.append(bits >> 16)
result.append(bits >> 8)
result.append(bits)
return result
@staticmethod
def _encode_utf(value):
"""
对字符串进行编码,编码格式utf-8
参见方法:com.alibaba.com.caucho.hessian.io.Hessian2Output#printString
:param value:
:return:
"""
result = []
for v in value:
ch = ord(v)
if ch < 0x80:
result.append(ch & 0xff)
elif ch < 0x800:
result.append((0xc0 + ((ch >> 6) & 0x1f)) & 0xff)
result.append((0x80 + (ch & 0x3f)) & 0xff)
else:
result.append((0xe0 + ((ch >> 12) & 0xf)) & 0xff)
result.append((0x80 + ((ch >> 6) & 0x3f)) & 0xff)
result.append((0x80 + (ch & 0x3f)) & 0xff)
return result
def _encode_str(self, value):
"""
对一个字符串进行编码
:param value:
:return:
"""
result = []
# 在进行网络传输操作时一律使用unicode进行操作
if isinstance(value, str):
value = value.decode('utf-8')
length = len(value)
if length <= 0x1f:
result.append(0x00 + length)
elif length <= 0x3ff:
result.append(0x30 + (length >> 8))
result.append(length)
else:
result.append(ord('S'))
result.append(length >> 8)
result.append(length)
result.extend(self._encode_utf(value))
return result
def _encode_object(self, value):
"""
对一个对象进行编码
:param value:
:return:
"""
result = []
path = value.get_path()
field_names = value.keys()
if path not in self.__classes:
result.append(ord('C'))
result.extend(self._encode_single_value(path))
result.extend(self._encode_single_value(len(field_names)))
for field_name in field_names:
result.extend(self._encode_single_value(field_name))
self.__classes.append(path)
class_id = self.__classes.index(path)
if class_id <= 0xf:
class_id += 0x60
class_id &= 0xff
result.append(class_id)
else:
result.append(ord('O'))
result.extend(self._encode_single_value(class_id))
for field_name in field_names:
result.extend(self._encode_single_value(value[field_name]))
return result
def _encode_list(self, value):
"""
对一个列表进行编码
:param value:
:return:
"""
result = []
length = len(value)
if length == 0:
# 没有值则无法判断类型,一律返回null
return self._encode_single_value(None)
if isinstance(value[0], bool):
_type = '[boolean'
elif isinstance(value[0], int):
_type = '[int'
elif isinstance(value[0], float):
_type = '[double'
elif isinstance(value[0], str):
_type = '[string'
elif isinstance(value[0], Object):
_type = '[object'
else:
raise HessianTypeError('Unknown list type: {}'.format(value[0]))
if length < 0x7:
result.append(0x70 + length)
if _type not in self.types:
self.types.append(_type)
result.extend(self._encode_single_value(_type))
else:
result.extend(self._encode_single_value(self.types.index(_type)))
else:
result.append(0x56)
if _type not in self.types:
self.types.append(_type)
result.extend(self._encode_single_value(_type))
else:
result.extend(self._encode_single_value(self.types.index(_type)))
result.extend(self._encode_single_value(length))
for v in value:
if type(value[0]) != type(v):
raise HessianTypeError('All elements in list must be the same type, first type'
' is {0} but current type is {1}'.format(type(value[0]), type(v)))
result.extend(self._encode_single_value(v))
return result
def _encode_single_value(self, value):
"""
根据hessian协议对单个变量进行编码
:param value:
:return:
"""
# 布尔类型
if isinstance(value, bool):
return self._encode_bool(value)
# 整型(包括长整型)
elif isinstance(value, int):
return self._encode_int(value)
# 浮点类型
elif isinstance(value, float):
return self._encode_float(value)
# 字符串类型
elif isinstance(value, (str, unicode)):
return self._encode_str(value)
# 对象类型
elif isinstance(value, Object):
return self._encode_object(value)
# 列表(list)类型,不可以使用tuple替代
elif isinstance(value, list):
return self._encode_list(value)
# null
elif value is None:
return [ord('N')]
else:
raise HessianTypeError('Unknown argument type: {}'.format(value))
def get_request_body_length(body):
"""
获取body的长度,并将其转为长度为4个字节的字节数组
:param body:
:return:
"""
return list(bytearray(struct.pack('!i', len(body))))
if __name__ == '__main__':
pass