blob: 060482120c8cf2e8fcb400ebf89103dae26518cd [file] [log] [blame]
/*
* 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.
*/
import debug from 'debug';
import Hessian from 'hessian.js';
import {toBytes8} from './byte';
import Context from './context';
import {DubboEncodeError} from './err';
import {isDevEnv} from './util';
const log = debug('dubbo:hessian:encoderV2');
//dubbo的序列化协议
//com.alibaba.dubbo.remoting.exchange.codec.ExchangeCodec
//encodeRequest
//header length
const DUBBO_HEADER_LENGTH = 16;
// magic header.
const DUBBO_MAGIC_HEADER = 0xdabb;
// message flag.
const FLAG_REQEUST = 0x80;
const FLAG_TWOWAY = 0x40;
//com.alibaba.dubbo.common.serialize.support.hessian.Hessian2Serialization中定义
const HESSIAN2_SERIALIZATION_CONTENT_ID = 2;
//dubbo最大的body序列化数据的大小
//com.alibaba.dubbo.common.Constants.DEAULT_PAY_LOAD
const DUBBO_DEFAULT_PAY_LOAD = 8 * 1024 * 1024; // 8M
export default class DubboEncoder {
constructor(ctx: Context) {
this._ctx = ctx;
if (isDevEnv) {
log(
'dubbo encode param request:%s',
JSON.stringify(this._ctx.request, null, 2),
);
}
}
private readonly _ctx: Context;
encode() {
const body = this.encodeBody();
const head = this.encodeHead(body.length);
log(`encode body length: ${body.length} bytes`);
return Buffer.concat([head, body]);
}
/**
* 根据协议,消息中写入16个字节的消息头:
* 1-2字节,固定的魔数
* 第3个字节,第7位存储数据类型是请求数据还是响应数据,其它8位存储序列化类型,约定和服务端的序列化-反序列化协议
* 5-12个字节,请求id
* 13-16个字节,请求数据长度
*
* @param payload body的长度
*/
private encodeHead(payload: number) {
//header
const header = Buffer.alloc(DUBBO_HEADER_LENGTH);
//set magic number
//magic high
header[0] = DUBBO_MAGIC_HEADER >>> 8;
//magic low
header[1] = DUBBO_MAGIC_HEADER & 0xff;
// set request and serialization flag.
header[2] = FLAG_REQEUST | HESSIAN2_SERIALIZATION_CONTENT_ID | FLAG_TWOWAY;
//requestId
this.setRequestId(header);
//check body length
if (payload > 0 && payload > DUBBO_DEFAULT_PAY_LOAD) {
throw new DubboEncodeError(
`Data length too large: ${payload}, max payload: ${DUBBO_DEFAULT_PAY_LOAD}`,
);
}
//body长度int-> 4个byte
header.writeUInt32BE(payload, 12);
return header;
}
private setRequestId(header) {
const {requestId} = this._ctx;
log(`encode header requestId: ${requestId}`);
const buffer = toBytes8(requestId);
header[4] = buffer[0];
header[5] = buffer[1];
header[6] = buffer[2];
header[7] = buffer[3];
header[8] = buffer[4];
header[9] = buffer[5];
header[10] = buffer[6];
header[11] = buffer[7];
}
private encodeBody() {
//hessian v2
const encoder = new Hessian.EncoderV2();
const {
dubboVersion,
dubboInterface,
version,
methodName,
methodArgs,
} = this._ctx;
//dubbo version
encoder.write(dubboVersion);
//path interface
encoder.write(dubboInterface);
//interface version
encoder.write(version);
//method name
encoder.write(methodName);
//supported dubbox
if (this._ctx.isSupportedDubbox) {
encoder.write(-1);
}
//parameter types
encoder.write(DubboEncoder.getParameterTypes(methodArgs));
//arguments
if (methodArgs && methodArgs.length) {
for (let arg of methodArgs) {
encoder.write(arg);
}
}
//attachments
encoder.write(this.getAttachments());
return encoder.byteBuffer._bytes.slice(0, encoder.byteBuffer._offset);
}
private static getParameterTypes(args: Array<any>) {
if (!(args && args.length)) {
return '';
}
const primitiveTypeRef = {
void: 'V',
boolean: 'Z',
byte: 'B',
char: 'C',
double: 'D',
float: 'F',
int: 'I',
long: 'J',
short: 'S',
};
const desc = [];
for (let arg of args) {
let type: string = arg['$class'];
//暂时不支持二维数组
//如果将来支持,这个地方要while判断下
if (type[0] === '[') {
//1. c is array
desc.push('[');
type = type.slice(1);
}
if (primitiveTypeRef[type]) {
//2. c is primitive
desc.push(primitiveTypeRef[type]);
} else {
//3. c is object
desc.push('L');
desc.push(type.replace(/\./gi, '/'));
desc.push(';');
}
}
return desc.join('');
}
private getAttachments() {
const {
requestId,
path,
dubboInterface,
group,
timeout,
version,
application: {name},
attachments,
} = this._ctx;
//merge dubbo attachments and customize attachments
const map = {
...{
path: path,
interface: dubboInterface,
version: version || '0.0.0',
},
...attachments,
};
group && (map['group'] = group);
timeout && (map['timeout'] = timeout);
name && (map['application'] = name);
let attachmentsHashMap = {
$class: 'java.util.HashMap',
$: map,
};
if (isDevEnv) {
log(
'request#%d attachment %s',
requestId,
JSON.stringify(attachmentsHashMap, null, 2),
);
}
return attachmentsHashMap;
}
}