org.zbus.net.http.Message Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of zbus Show documentation
Show all versions of zbus Show documentation
lightweight message queue, service bus
/**
* The MIT License (MIT)
* Copyright (c) 2009-2015 HONG LEIMING
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package org.zbus.net.http;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.URL;
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.kit.log.Logger;
import org.zbus.net.Client.MsgHandler;
import org.zbus.net.Invoker;
import org.zbus.net.Sync.Id;
import org.zbus.net.core.IoBuffer;
/**
* HTTP Protocol Message, stands for both request and response formats.
*
* Message is NOT standard HTTP protocol, but compatible to HTTP standard format.
* Message may extend HTTP protocol in the header part, for example: mq: MyMQ\r\n
* means a header extension of Key=mq, Value=MyMQ.
*
* @author rushmore (洪磊明)
*
*/
public class Message implements Id {
private static final Logger log = Logger.getLogger(Message.class);
private static final String DEFAULT_ENCODING = "UTF-8";
public static final String HEARTBEAT = "heartbeat"; //心跳消息
//使用到的标准HTTP头部
public static final String REMOTE_ADDR = "remote-addr";
public static final String CONTENT_LENGTH = "content-length";
public static final String CONTENT_TYPE = "content-type";
//常见扩展HTTP协议头部
public static final String CMD = "cmd";
public static final String SUB_CMD = "sub_cmd";
public static final String MQ = "mq";
public static final String SENDER = "sender";
public static final String RECVER = "recver";
public static final String ID = "id"; //消息ID
public static final String RAWID = "rawid"; //原始消息ID
public static final String SERVER = "server";
public static final String TOPIC = "topic"; //使用,分隔
public static final String ACK = "ack";
public static final String WINDOW = "window";
public static final String ENCODING = "encoding";
public static final String DELAY = "delay";
public static final String TTL = "ttl";
public static final String KEY = "key";
public static final String KEY_GROUP = "key_group";
//MQ copy, Master-Slave
public static final String MASTER_MQ = "master_mq";
public static final String MASTER_TOKEN = "master_token";
//HTTP协议第一行(请求串或者返回状态码)
private Meta meta = new Meta();
//HTTP协议Key-Value头部
private Map head = new ConcurrentHashMap();
//HTTP消息体
private byte[] body;
public Message(){
setBody((byte[])null);
setHead("connection", "Keep-Alive"); //since 6.3.0/6.2.8
}
public Message(String body){
setBody(body);
setHead("connection", "Keep-Alive");
}
public Message(byte[] body){
setBody(body);
setHead("connection", "Keep-Alive");
}
public static Message copyWithoutBody(Message msg){
Message res = new Message();
res.meta = new Meta(msg.meta);
res.head = new ConcurrentHashMap(msg.head);
res.body = msg.body;
return res;
}
/**
* HTTP请求串
* eg. http://localhost/hello?xx=yy
* requestString=/hello?xx=yy
* @return
*/
public String getRequestString(){
return this.meta.requestString;
}
public void setRequestString(String requestString){
this.meta.requestString = requestString;
this.meta.status = null; //once requestString is set, it becomes a request message
}
/**
* HTTP响应状态码
* e.g. 200 OK
* status=200
* @return
*/
public String getResponseStatus() {
return meta.status;
}
public Message setResponseStatus(String status) {
meta.status = status;
return this;
}
public Message setResponseStatus(int status){
return setResponseStatus(status+"");
}
public String getStatus(){
return getResponseStatus();
}
public void setStatus(int status){
setResponseStatus(status);
}
public String getMethod(){
return meta.getMethod();
}
public void setMethod(String method){
this.meta.setMethod(method);
}
/**
* HTTP请求串
* eg. http://localhost/hello?xx=yy
* requestPath=/hello
* @return
*/
public String getRequestPath(){
return this.meta.requestPath;
}
public void setRequestPath(String path){
meta.setRequestPath(path);
}
public Map getRequestParams(){
return meta.requestParams;
}
public String getRequestParam(String key){
return meta.getRequestParam(key);
}
public void setRequestParam(String key, String value){
meta.setRequestParam(key, value);
}
public Map getHead() {
return head;
}
public void setHead(Map head) {
this.head = head;
}
public String getHead(String key){
return this.head.get(key);
}
public String getHead(String key, String defaultValue){
String res = this.head.get(key);
if(res == null) return defaultValue;
return res;
}
public void setHead(String key, String value){
if(value == null) return;
this.head.put(key, value);
}
public void setHead(String key, Object value){
if(value == null) return;
this.head.put(key, value.toString());
}
public String removeHead(String key){
return this.head.remove(key);
}
public byte[] getBody() {
byte[] b = body;
String bodyOfHead = getHead("body");
if(b == null && bodyOfHead != null){
b = bodyOfHead.getBytes();
}
return b;
}
public Message setBody(byte[] body) {
int len = 0;
if( body != null){
len = body.length;
}
this.setHead(CONTENT_LENGTH, ""+len);
this.body = body;
return this;
}
public Message setBody(String body, String encoding){
try {
return setBody(body.getBytes(encoding));
} catch (UnsupportedEncodingException e) { //just ignore
return setBody(body);
}
}
public Message setBody(String body){
String encoding = this.getEncoding();
if(encoding == null){
encoding = DEFAULT_ENCODING;
}
try {
return setBody(body.getBytes(encoding));
} catch (UnsupportedEncodingException e) { //just ignore
return setBody(body.getBytes());
}
}
public Message setBody(String format, Object ...args) {
this.setBody(String.format(format, args));
return this;
}
public Message setJsonBody(String body){
return this.setJsonBody(body.getBytes());
}
public Message setJsonBody(byte[] body){
this.setHead(CONTENT_TYPE, "application/json");
this.setBody(body);
return this;
}
public String getBodyString() {
if (this.getBody() == null) return null;
String encoding = this.getEncoding();
if(encoding == null){
encoding = DEFAULT_ENCODING;
}
return getBodyString(encoding);
}
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){
ByteArrayInputStream inputStream = null;
InputStreamReader inputStreamReader = null;
BufferedReader in = null;
try{
inputStream = new ByteArrayInputStream(data, offset, size);
inputStreamReader = new InputStreamReader(inputStream);
in = new BufferedReader(inputStreamReader);
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();
}
} catch(IOException e){
log.error(e.getMessage(), e);
} finally {
if(in != null){
try { in.close(); } catch (IOException e) {}
}
if(inputStreamReader != null){
try { inputStreamReader.close(); } catch (IOException e) {}
}
if(inputStream != null){
try { inputStream.close(); } catch (IOException e) {}
}
}
}
public String getCmd() {
return this.getHead(CMD);
}
public Message setCmd(String value) {
this.setHead(CMD, value);
return this;
}
public String getSubCmd() {
return this.getHead(SUB_CMD);
}
public Message setSubCmd(String value) {
this.setHead(SUB_CMD, value);
return this;
}
public String getServer(){
return this.getHead(SERVER);
}
public void setServer(String value){
this.setHead(SERVER, value);
}
public String getSender() {
return this.getHead(SENDER);
}
public Message setSender(String value) {
this.setHead(SENDER, value);
return this;
}
public String getRecver() {
return this.getHead(RECVER);
}
public Message setRecver(String value) {
this.setHead(RECVER, value);
return this;
}
public String getRemoteAddr() {
return this.getHead(REMOTE_ADDR);
}
public Message setRemoteAddr(String value) {
this.setHead(REMOTE_ADDR, value);
return this;
}
public String getEncoding() {
return this.getHead(ENCODING);
}
public Message setEncoding(String encoding) {
this.setHead(ENCODING, encoding);
return this;
}
public String getDelay() {
return this.getHead(DELAY);
}
public Message setDelay(String value) {
this.setHead(DELAY, value);
return this;
}
public String getTtl() {
return this.getHead(TTL);
}
public Message setTtl(String value) {
this.setHead(TTL, value);
return this;
}
public String getId() {
return this.getHead(ID);
}
public void setId(String msgId) {
if(msgId == null) return;
this.setHead(ID, msgId);
}
public void setId(long id){
setId(""+id);
}
public String getRawId() {
return this.getHead(RAWID);
}
public Message setRawId(String value) {
if(value == null) return this;
this.setHead(RAWID, value);
return this;
}
public boolean isAck() {
String ack = this.getHead(ACK);
if(ack == null) return true; //default to true
ack = ack.trim().toLowerCase();
return ack.equals("1") || ack.equals("true");
}
public void setAck(boolean ack){
String value = ack? "1":"0";
this.setHead(ACK, value);
}
public String getMq(){
String value = this.getHead(MQ);
return value;
}
public Message setMq(String mq) {
this.setHead(MQ, mq);
return this;
}
public String getTopic() {
return getHead(TOPIC);
}
public Message setTopic(String topic) {
this.setHead(TOPIC, topic);
return this;
}
public String getWindow() {
return getHead(WINDOW);
}
public Message setWindow(int window) {
this.setHead(WINDOW, ""+window);
return this;
}
public String getKey() {
return getHead(KEY);
}
public Message setKey(String value) {
this.setHead(KEY, value);
return this;
}
public String getKeyGroup() {
return getHead(KEY_GROUP);
}
public Message setKeyGroup(String value) {
this.setHead(KEY_GROUP, value);
return this;
}
public String getMasterMq() {
return getHead(MASTER_MQ);
}
public Message setMasterMq(String value) {
this.setHead(MASTER_MQ, value);
return this;
}
public String getMasterToken() {
return getHead(MASTER_TOKEN);
}
public Message setMasterToken(String value) {
this.setHead(MASTER_TOKEN, value);
return this;
}
public boolean isStatus200() {
return "200".equals(this.getResponseStatus());
}
public boolean isStatus404() {
return "404".equals(this.getResponseStatus());
}
public boolean isStatus500() {
return "500".equals(this.getResponseStatus());
}
public boolean isStatus406() {
return "406".equals(this.getResponseStatus());
}
protected String getBodyPrintString() {
if (this.body == null)
return null;
if (this.body.length > 1024) {
return new String(this.body, 0, 1024) + "...";
} else {
return getBodyString();
}
}
static final byte[] CLCR = "\r\n".getBytes();
static final byte[] KV_SPLIT = ": ".getBytes();
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(meta+"\r\n");
List keys = new ArrayList(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 byte[] toBytes(){
IoBuffer bb = toIoBuffer();
byte[] b = new byte[bb.remaining()];
bb.readBytes(b);
return b;
}
public IoBuffer toIoBuffer(){
IoBuffer bb = meta.toIoBuffer();
bb.writeBytes(CLCR);
Iterator> it = head.entrySet().iterator();
while(it.hasNext()){
Entry kv = it.next();
bb.writeBytes(kv.getKey().getBytes());
bb.writeBytes(KV_SPLIT);
bb.writeBytes(kv.getValue().getBytes());
bb.writeBytes(CLCR);
}
bb.writeBytes(CLCR);
if(body != null){
bb.writeBytes(body);
}
bb.flip();
return bb;
}
static class Meta{
//HTTP响应头部: 状态(200)
String status; //根据status是否设置来决定Meta是请求还是应答
//HTTP请求头部: 方法(GET/POST)-RequestString-KV参数
String method = "GET";
String requestString = "/"; //请求串 (最终决定,下面两个辅助动态更新)
String requestPath = requestString; //请求路径
Map requestParams; //请求参数KV
static Set httpMethod = new HashSet();
static Map httpStatus = new HashMap();
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() {
IoBuffer buf = toIoBuffer().flip();
return new String(buf.array(), 0, buf.remaining());
}
final static byte[] BLANK = " ".getBytes();
final static byte[] PREFIX = "HTTP/1.1 ".getBytes();
final static byte[] SUFFIX = " HTTP/1.1".getBytes();
public IoBuffer toIoBuffer(){
IoBuffer bb = IoBuffer.allocate(1024);
if(this.status != null){
String desc = httpStatus.get(this.status);
if(desc == null){
desc = "Unknown Status";
}
bb.writeBytes(PREFIX);
bb.writeString(status);
bb.writeBytes(BLANK);
bb.writeString(desc);
} else {
String method = this.method;
if(method == null) method = "";
bb.writeString(method);
bb.writeBytes(BLANK);
String requestString = this.requestString;
if(requestString == null) requestString = "";
bb.writeString(requestString);
bb.writeBytes(SUFFIX);
}
return bb;
}
public Meta(){ }
public Meta(Meta m){
this.requestString = m.requestString;
this.requestPath = m.requestPath;
this.method = m.method;
this.status = m.status;
if(m.requestParams != null){
this.requestParams = new HashMap(m.requestParams);
}
}
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.requestString = st.nextToken();
decodeRequestString(this.requestString);
}
public String getMethod(){
return this.method;
}
public void setMethod(String method){
this.method = method;
}
public void setRequestString(String requestString){
this.requestString = requestString;
decodeRequestString(requestString);
}
private void calcRequestString(){
StringBuilder sb = new StringBuilder();
if(this.requestPath != null){
sb.append(this.requestPath);
}
if(this.requestParams != null){
sb.append("?");
Iterator> it = requestParams.entrySet().iterator();
while(it.hasNext()){
Entry e = it.next();
sb.append(e.getKey()+"="+e.getValue());
if(it.hasNext()){
sb.append("&&");
}
}
}
this.requestString = sb.toString();
}
private void decodeRequestString(String commandString){
int idx = commandString.indexOf('?');
if(idx < 0){
this.requestPath = urlDecode(commandString);
} else {
this.requestPath = commandString.substring(0, idx);
}
if(this.requestPath.endsWith("/")){
this.requestPath = this.requestPath.substring(0, this.requestPath.length()-1);
}
if(idx < 0) return;
if(this.requestParams == null){
this.requestParams = new ConcurrentHashMap();
}
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.requestParams.put(urlDecode(e.substring(0, sep)).trim(),
urlDecode(e.substring(sep + 1)));
} else {
this.requestParams.put(urlDecode(e).trim(), "");
}
}
}
private String urlDecode(String str) {
String decoded = null;
try {
decoded = URLDecoder.decode(str, "UTF8");
} catch (UnsupportedEncodingException ignored) {
}
return decoded;
}
public String getRequestParam(String key){
if(requestParams == null) return null;
return requestParams.get(key);
}
public String getRequestParam(String key, String defaultValue){
String value = getRequestParam(key);
if(value == null){
value = defaultValue;
}
return value;
}
public void setRequestPath(String path){
this.requestPath = path;
calcRequestString();
}
public void setRequestParam(String key, String value){
if(requestParams == null){
requestParams = new HashMap();
}
requestParams.put(key, value);
calcRequestString();
}
}
public static interface MessageHandler extends MsgHandler { }
public static interface MessageInvoker extends Invoker { }
public static interface MessageProcessor {
Message process(Message request);
}
public Message invokeSimpleHttp() throws IOException {
String server = this.getServer();
if(server == null){
throw new IllegalArgumentException("request missing target server");
}
String format = "http://%s%s";
if(server.startsWith("http://")){
format = "%s%s";
}
String urlString = String.format(format, server, this.getRequestString());
URL url = new URL(urlString);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
for(Entry e : this.getHead().entrySet()){
conn.setRequestProperty(e.getKey(), e.getValue());
}
conn.setRequestMethod(this.getMethod());
if(this.getBody() != null && this.getBody().length > 0){
conn.setDoOutput(true);
conn.getOutputStream().write(this.getBody());
}
int code = conn.getResponseCode();
Message res = new Message();
res.setResponseStatus(code);
for (Entry> header : conn.getHeaderFields().entrySet()){
if(header.getKey() == null) continue;
List values = header.getValue();
String value = null;
if(values.size() == 1){
value = values.get(0);
} else if(values.size() > 1){
value = "[";
for(int i=0; i 0){
byte[] body = new byte[bodyLen];
int size = bodyStream.read(body);
if(size == bodyLen){
res.setBody(body);
} else {
throw new IllegalStateException("body reading size error");
}
}
}
conn.disconnect();
return res;
}
public static void main(String[] args) throws Exception {
Message req = new Message();
req.setServer("127.0.0.1:8080");
req.setRequestString("/hello");
System.err.println(req);
Message res = req.invokeSimpleHttp();
System.out.println(res);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy