package org.zbus.common.remoting;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Serializable;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.concurrent.ConcurrentHashMap;
import org.zbus.common.logging.Logger;
import org.zbus.common.logging.LoggerFactory;
public class Message implements Serializable {
private static final long serialVersionUID = 4379223525215626137L;
private static final Logger log = LoggerFactory.getLogger(Message.class);
public static final String HEARTBEAT = "heartbeat"; //心跳消息
//使用到的标准HTTP头部
public static final String HEADER_REMOTE_ADDR = "remote-addr";
public static final String HEADER_CONTENT_ENCODING = "content-encoding";
public static final String HEADER_CONTENT_LENGTH = "content-length";
public static final String HEADER_CONTENT_TYPE = "content-type";
//扩展HTTP协议头部
public static final String HEADER_CMD = "cmd";
public static final String HEADER_SUBCMD = "sub_cmd";
public static final String HEADER_MQ = "mq";
public static final String HEADER_MQ_REPLY = "mq_reply";
public static final String HEADER_MSGID = "msgid"; //消息ID
public static final String HEADER_MSGID_RAW = "msgid_raw"; //原始消息ID
public static final String HEADER_TOKEN = "token";
public static final String HEADER_BROKER = "broker";
public static final String HEADER_TOPIC = "topic"; //使用,分隔
public static final String HEADER_ACK = "ack";
public static final String HEADER_WINDOW = "window";
public static final String HEADER_REPLY_CODE = "reply_code";
protected Meta meta = new Meta();
protected Map<String, String> head = new ConcurrentHashMap<String, String>();
protected byte[] body;
public Message(){
setBody((byte[])null);
}
public static Message copyWithoutBody(Message msg){
Message res = new Message();
res.meta = new Meta(msg.meta);
res.head = new HashMap<String, String>(msg.head);
res.body = msg.body;
return res;
}
public String getMetaString() {
return meta.toString();
}
public Meta getMeta(){
return meta;
}
public void setMeta(String meta) {
this.meta = new Meta(meta);
}
public void setMeta(Meta meta) {
this.meta = meta;
}
public Map<String, String> getHead() {
return head;
}
public void setHead(Map<String, String> head) {
this.head = head;
}
public String getHead(String key){
return this.head.get(key);
}
public void setHead(String key, String value){
if(value == null) return;
this.head.put(key, value);
}
public String removeHead(String key){
return this.head.remove(key);
}
public String getParam(String key){
return meta.getParam(key);
}
public String getHeadOrParam(String key){
String value = getHead(key);
if(value == null){
value = getParam(key);
}
return value;
}
public String getHeadOrParam(String key, String defaultValue) {
String value = getHeadOrParam(key);
if(value == null){
value = defaultValue;
}
return value;
}
public byte[] getBody() {
byte[] b = body;
String bodyOfHead = getHead("body");
if(b == null && bodyOfHead != null){
b = bodyOfHead.getBytes();
}
return b;
}
public void setBody(byte[] body) {
int len = 0;
if( body != null){
len = body.length;
}
this.setHead(HEADER_CONTENT_LENGTH, ""+len);
this.body = body;
}
public void setBody(String body){
setBody(body.getBytes());
}
public Message setBody(String format, Object ...args) {
this.setBody(String.format(format, args));
return this;
}
public void setJsonBody(String body){
this.setJsonBody(body.getBytes());
}
public void setJsonBody(byte[] body){
this.setHead(HEADER_CONTENT_TYPE, "application/json");
this.setBody(body);
}
public String getBodyString() {
if (this.getBody() == null) return null;
return new String(this.getBody());
}
public String getBodyString(String encoding) {
if (this.getBody() == null) return null;
try {
return new String(this.getBody(), encoding);
} catch (UnsupportedEncodingException e) {
return new String(this.getBody());
}
}
//////////////////////////////////////////////////////////////
public void decodeHeaders(byte[] data, int offset, int size){
try{
BufferedReader in = new BufferedReader(new InputStreamReader(new
ByteArrayInputStream(data, offset, size)));
String meta = in.readLine();
if(meta == null) return;
this.meta = new Meta(meta);
String line = in.readLine();
while (line != null && line.trim().length() > 0) {
int p = line.indexOf(':');
if (p >= 0){
head.put(line.substring(0, p).trim().toLowerCase(), line.substring(p + 1).trim());
}
line = in.readLine();
}
//合并: header优先,url参数次之
if(this.meta.params != null){
for(Map.Entry<String, String> kv : this.meta.params.entrySet()){
String key = kv.getKey().toLowerCase();
if(!head.containsKey(key)){
head.put(key, kv.getValue());
}
}
}
} catch(IOException e){
log.error(e.getMessage(), e);
}
}
public int estimatedSize(){
int size = 0;
size += meta.toString().length();
Iterator<Entry<String, String>> iter = head.entrySet().iterator();
while(iter.hasNext()){
Entry<String, String> e = iter.next();
size += e.getKey().length() + e.getKey().length();
}
if(body != null){
size += body.length;
}
return size;
}
public String getCommand() {
return this.getHeadOrParam(HEADER_CMD);
}
public Message setCommand(String value) {
this.setHead(HEADER_CMD, value);
return this;
}
public String getSubCommand() {
return this.getHeadOrParam(HEADER_SUBCMD);
}
public Message setSubCommand(String value) {
this.setHead(HEADER_SUBCMD, value);
return this;
}
public String getBroker(){
return this.getHeadOrParam(HEADER_BROKER);
}
public void setBroker(String value){
this.setHead(HEADER_BROKER, value);
}
public String getMqReply() {
return this.getHeadOrParam(HEADER_MQ_REPLY);
}
public Message setMqReply(String value) {
this.setHead(HEADER_MQ_REPLY, value);
return this;
}
public String getEncoding() {
return this.getHeadOrParam(HEADER_CONTENT_ENCODING);
}
public Message setEncoding(String encoding) {
this.setHead(HEADER_CONTENT_ENCODING, encoding);
return this;
}
public String getMsgId() {
return this.getHeadOrParam(HEADER_MSGID);
}
public Message setMsgId(String msgId) {
if(msgId == null) return this;
this.setHead(HEADER_MSGID, msgId);
return this;
}
public String getMsgIdRaw() {
return this.getHeadOrParam(HEADER_MSGID_RAW);
}
public Message setMsgIdRaw(String value) {
if(value == null) return this;
this.setHead(HEADER_MSGID_RAW, value);
return this;
}
public boolean isAck() {
String ack = this.getHeadOrParam(HEADER_ACK);
if(ack == null) return true; //默认ack为true
ack = ack.trim().toLowerCase();
return ack.equals("1") || ack.equals("true");
}
public void setAck(boolean ack){
String value = ack? "1":"0";
this.setHead(HEADER_ACK, value);
}
public String getMq(){
String value = this.getHeadOrParam(HEADER_MQ);
if(value == null){
value = getPath();
}
return value;
}
public String getUri(){
return this.meta.uri;
}
public String getPath(){
return this.meta.path;
}
public Message setMq(String mq) {
this.setHead(HEADER_MQ, mq);
return this;
}
public String getToken() {
return this.getHeadOrParam(HEADER_TOKEN);
}
public Message setToken(String token) {
this.setHead(HEADER_TOKEN, token);
return this;
}
public String getTopic() {
return getHeadOrParam(HEADER_TOPIC);
}
public Message setTopic(String topic) {
this.setHead(HEADER_TOPIC, topic);
return this;
}
public String getWindow() {
return getHeadOrParam(HEADER_WINDOW);
}
public Message setWindow(int window) {
this.setHead(HEADER_WINDOW, ""+window);
return this;
}
public String getReplyCode() {
return getHeadOrParam(HEADER_REPLY_CODE);
}
public Message setReplyCode(String value) {
this.setHead(HEADER_REPLY_CODE, value);
return this;
}
public String getStatus() {
return meta.status;
}
public Message setStatus(String status) {
meta.status = status;
return this;
}
public boolean isStatus200() {
return "200".equals(this.getStatus());
}
public boolean isStatus404() {
return "404".equals(this.getStatus());
}
public boolean isStatus500() {
return "500".equals(this.getStatus());
}
protected String getBodyPrintString() {
if (this.body == null)
return null;
if (this.body.length > 1024) {
return new String(this.body, 0, 1024) + "...";
} else {
return getBodyString();
}
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(meta+"\r\n");
List<String> keys = new ArrayList<String>(head.keySet());
Collections.sort(keys);
for(String key : keys){
String val = head.get(key);
sb.append(key+": "+val+"\r\n");
}
sb.append("\r\n");
String bodyString = getBodyPrintString();
if(bodyString != null){
sb.append(bodyString);
}
return sb.toString();
}
public static void main(String[] args){
Message msg = new Message();
msg.setStatus("200");
msg.setStatus(null);
System.out.println(msg);
}
}
class Meta implements Serializable{
private static final long serialVersionUID = -8557063231118504061L;
//HTTP响应头部: 状态(200)
String status; //根据status是否设置来决定Meta是请求还是应答
//HTTP请求头部: 方法(GET/POST)-RequestString-KV参数
String method = "GET";
String uri = "/";
//请求分析出来的两个部分:path + kv组
String path;
Map<String,String> params;
static Set<String> httpMethod = new HashSet<String>();
static Map<String,String> httpStatus = new HashMap<String, String>();
static{
httpMethod.add("GET");
httpMethod.add("POST");
httpMethod.add("PUT");
httpMethod.add("DELETE");
httpMethod.add("HEAD");
httpMethod.add("OPTIONS");
httpStatus.put("101", "Switching Protocols");
httpStatus.put("200", "OK");
httpStatus.put("201", "Created");
httpStatus.put("202", "Accepted");
httpStatus.put("204", "No Content");
httpStatus.put("206", "Partial Content");
httpStatus.put("301", "Moved Permanently");
httpStatus.put("304", "Not Modified");
httpStatus.put("400", "Bad Request");
httpStatus.put("401", "Unauthorized");
httpStatus.put("403", "Forbidden");
httpStatus.put("404", "Not Found");
httpStatus.put("405", "Method Not Allowed");
httpStatus.put("416", "Requested Range Not Satisfiable");
httpStatus.put("500", "Internal Server Error");
}
@Override
public String toString() {
//如果status存在,理解为响应包,否则默认就是请求包
if(this.status != null){
String desc = httpStatus.get(this.status);
if(desc == null){
desc = "Unknown Status";
}
return String.format("HTTP/1.1 %s %s", status, desc);
}
String method = this.method;
String uri = this.uri;
if(this.method == null) method = "";
if(this.uri == null) uri = "";
return String.format("%s %s HTTP/1.1", method, uri);
}
public Meta(){}
public Meta(Meta m){
this.uri = m.uri;
this.path = m.path;
this.method = m.method;
this.status = m.status;
if(m.params != null){
this.params = new HashMap<String, String>(m.params);
}
}
public Meta(String meta){
if("".equals(meta)){
return;
}
StringTokenizer st = new StringTokenizer(meta);
String firstWord = st.nextToken();
if(firstWord.toUpperCase().startsWith("HTTP")){ //理解为响应
this.status = st.nextToken();
return;
}
//理解为请求
this.method = firstWord;
this.uri = st.nextToken();
decodeURI(this.uri);
}
private void decodeURI(String commandString){
int idx = commandString.indexOf('?');
if(idx < 0){
this.path = decodeUrl(commandString);
} else {
this.path = commandString.substring(0, idx);
}
if(this.path.startsWith("/")){
this.path = this.path.substring(1);
}
if(idx < 0) return;
this.params = new HashMap<String, String>();
String paramString = commandString.substring(idx+1);
StringTokenizer st = new StringTokenizer(paramString, "&");
while (st.hasMoreTokens()) {
String e = st.nextToken();
int sep = e.indexOf('=');
if (sep >= 0) {
this.params.put(decodeUrl(e.substring(0, sep)).trim(),
decodeUrl(e.substring(sep + 1)));
} else {
this.params.put(decodeUrl(e).trim(), "");
}
}
}
private String decodeUrl(String str) {
String decoded = null;
try {
decoded = URLDecoder.decode(str, "UTF8");
} catch (UnsupportedEncodingException ignored) {
}
return decoded;
}
public String getParam(String key){
if(params == null) return null;
return params.get(key);
}
public String getParam(String key, String defaultValue){
String value = getParam(key);
if(value == null){
value = defaultValue;
}
return value;
}
}