commit cd7e4c79c1e3b6b602ddaaf8317f0bb7d7b506e8 Author: newpanjing Date: Tue Mar 14 15:32:26 2017 +0800 初始化提交 1.请求支持postBody、post、get三种方式 2.支持网络速率、延迟、流量统计 diff --git a/wezoz-nat-client/.settings/org.eclipse.core.resources.prefs b/wezoz-nat-client/.settings/org.eclipse.core.resources.prefs new file mode 100644 index 0000000..99f26c0 --- /dev/null +++ b/wezoz-nat-client/.settings/org.eclipse.core.resources.prefs @@ -0,0 +1,2 @@ +eclipse.preferences.version=1 +encoding/=UTF-8 diff --git a/wezoz-nat-client/.settings/org.eclipse.jdt.core.prefs b/wezoz-nat-client/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 0000000..ec4300d --- /dev/null +++ b/wezoz-nat-client/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,5 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7 +org.eclipse.jdt.core.compiler.compliance=1.7 +org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning +org.eclipse.jdt.core.compiler.source=1.7 diff --git a/wezoz-nat-client/.settings/org.eclipse.m2e.core.prefs b/wezoz-nat-client/.settings/org.eclipse.m2e.core.prefs new file mode 100644 index 0000000..f897a7f --- /dev/null +++ b/wezoz-nat-client/.settings/org.eclipse.m2e.core.prefs @@ -0,0 +1,4 @@ +activeProfiles= +eclipse.preferences.version=1 +resolveWorkspaceProjects=true +version=1 diff --git a/wezoz-nat-client/pom.xml b/wezoz-nat-client/pom.xml new file mode 100644 index 0000000..f2616aa --- /dev/null +++ b/wezoz-nat-client/pom.xml @@ -0,0 +1,63 @@ + + 4.0.0 + + com.wezoz.nat + wezoz-nat-client + 0.0.1-SNAPSHOT + jar + + wezoz-nat-client + https://www.wezoz.com + + + UTF-8 + 1.7 + 1.7 + 1.7 + + + + + io.socket + socket.io-client + 0.8.3 + + + + + com.alibaba + fastjson + 1.2.24 + + + + log4j + log4j + 1.2.17 + + + + org.apache.httpcomponents + httpclient + 4.5 + + + org.apache.httpcomponents + httpmime + 4.5 + + + + commons-codec + commons-codec + 1.9 + + + commons-io + commons-io + 2.4 + + + + diff --git a/wezoz-nat-client/src/main/java/com/wezoz/nat/CallListener.java b/wezoz-nat-client/src/main/java/com/wezoz/nat/CallListener.java new file mode 100644 index 0000000..9c623a9 --- /dev/null +++ b/wezoz-nat-client/src/main/java/com/wezoz/nat/CallListener.java @@ -0,0 +1,35 @@ +package com.wezoz.nat; + +public interface CallListener { + + /** + * 状态改变 + * + * @param info + */ + public void statusCall(String info); + + /** + * 事件回调 + * + * @param info + */ + public void eventCall(String info); + + /** + * 流量统计 + * + * @param traffic + */ + public void trafficCall(long traffic); + + public void speedCall(long speed); + + /** + * 关闭 + */ + public void onClose(); + + public void ping(long ms); + +} \ No newline at end of file diff --git a/wezoz-nat-client/src/main/java/com/wezoz/nat/LocalServer.java b/wezoz-nat-client/src/main/java/com/wezoz/nat/LocalServer.java new file mode 100644 index 0000000..594871c --- /dev/null +++ b/wezoz-nat-client/src/main/java/com/wezoz/nat/LocalServer.java @@ -0,0 +1,318 @@ +package com.wezoz.nat; + +import java.sql.Time; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import java.util.Timer; +import java.util.TimerTask; + +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpPost; +import org.apache.log4j.Logger; +import org.json.JSONException; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import com.wezoz.nat.form.MainForm; +import com.wezoz.nat.utils.HttpClientUtils; + +import io.socket.client.IO; +import io.socket.client.IO.Options; +import io.socket.client.Socket; + +public class LocalServer { + + private static final String HTTP_REQUEST = "httpRequest"; + + private static final String HTTP_RESPONSE = "httpResponse"; + + private static final String BIND_DOMAIN = "bindDomain"; + + private static final String BIND_DOMAIN_NOTICE = "bindDomainNotice"; + + private static final String PING = "clientPing"; + + private static final String PING_NOTICE = "clientPingNotice"; + + private Logger logger = Logger.getLogger(getClass()); + + private Socket socket; + + private String server; + + private String forward; + + private String domain; + + private long traffic = 0;// 流量统计 + + private long speed = 0;// 实时网速统计 + + public void setDomain(String domain) { + this.domain = domain; + } + + public void setForward(String forward) { + this.forward = forward; + } + + public void setServer(String server) { + this.server = server; + } + + private CallListener callListener; + + public void setCallListener(CallListener callListener) { + this.callListener = callListener; + } + + public void ping() { + if (socket == null) { + return; + } + socket.emit(PING, System.currentTimeMillis()); + } + + public void bindDomain() { + + socket.emit(BIND_DOMAIN, domain); + + } + + public void start() throws Exception { + + Options opts = new Options(); + opts.transports = new String[] { "websocket", "polling" }; + socket = IO.socket(server, opts); + + Map eventMapper = new HashMap(); + + eventMapper.put(Socket.EVENT_DISCONNECT, "断开连接"); + eventMapper.put(Socket.EVENT_ERROR, "断开错误"); + eventMapper.put(Socket.EVENT_CONNECTING, "正在连接服务器"); + eventMapper.put(Socket.EVENT_CONNECT_TIMEOUT, "连接服务器超时"); + eventMapper.put(Socket.EVENT_RECONNECTING, "自动重连服务器"); + eventMapper.put(Socket.EVENT_RECONNECT, "准备重连服务器"); + eventMapper.put(Socket.EVENT_CONNECT, "连接服务器成功"); + eventMapper.put(HTTP_REQUEST, null); + eventMapper.put(BIND_DOMAIN_NOTICE, null); + eventMapper.put(PING_NOTICE, null); + Set keys = eventMapper.keySet(); + // 注册事件和提示信息 + for (String k : keys) { + SocketListener socketListener = new SocketListener() { + + @Override + public void eventCall(String eventName, String message, Object... args) { + if (null != message) { + callListener.eventCall("[" + eventName + "]:" + message); + } + switch (eventName) { + case Socket.EVENT_CONNECT: + callListener.statusCall("连接服务器成功"); + LocalServer.this.bindDomain(); + LocalServer.this.ping(); + break; + case HTTP_REQUEST: + handlerRequest(args); + break; + case BIND_DOMAIN_NOTICE: + handlerBindDomain(args); + break; + case PING_NOTICE: + handlerPing(args); + break; + default: + break; + } + } + + }; + socketListener.setEventName(k); + socketListener.setMessage(eventMapper.get(k)); + socket.on(k, socketListener); + } + socket.connect(); + + // 注册回调事件,实时统计网络速率 + + TimerTask task = new TimerTask() { + + @Override + public void run() { + long temp = speed; + speed = 0; + callListener.speedCall(temp); + + } + }; + Timer timer = new Timer(); + timer.scheduleAtFixedRate(task, 0, 1000); + new Timer().scheduleAtFixedRate(new TimerTask() { + + @Override + public void run() { + ping(); + } + }, 0, 10000); + } + + private void handlerPing(Object[] args) { + long time = (long) args[0]; + long ms = System.currentTimeMillis() - time; + callListener.ping(ms); + } + + private void handlerBindDomain(Object... args) { + try { + org.json.JSONObject jsonObject = (org.json.JSONObject) args[0]; + int code = jsonObject.getInt("code"); + String msg = jsonObject.getString("msg"); + if (code == 1000) { + // 弹出地址 + } else { + // 断开链接 + socket.close(); + callListener.onClose(); + } + callListener.eventCall("[绑定域名]:" + msg); + } catch (Exception e) { + logger.error(e); + } + } + + /** + * 处理远程的http请求 + * + * @param args + */ + private void handlerRequest(Object... args) { + org.json.JSONObject object = (org.json.JSONObject) args[0]; + JSONObject request = JSON.parseObject(object.toString()); + String url = request.getString("url"); + Map headers = request.getJSONObject("headers"); + headers.remove("content-length"); + String method = request.getString("method"); + String eventName = request.getString("eventName"); + + Map params = request.getJSONObject("params"); + + String reqUrl = forward + url; + logger.info("收到请求: methon=" + method + " url=" + reqUrl); + if (callListener != null) { + callListener.eventCall("[远程请求]: methon=" + method + " url=" + reqUrl + " params:" + params + " headers:" + headers); + } + Response response = null; + // 发送请求 + // 替换host + Set keys = headers.keySet(); + if ("POST".equals(method.toUpperCase())) { + + // 查看是否是json或者xml请求一类 + String encoding = "utf-8"; + String contentType = null; + if (headers.get("content-type") != null) { + contentType = headers.get("content-type").toString(); + String[] array = contentType.split(";"); + contentType = array[0]; + if (array.length > 1) { + String[] charsets = array[1].split("="); + if (charsets.length > 1) { + encoding = charsets[1]; + } + } + } + HttpPost post = new HttpPost(reqUrl); + for (String k : keys) { + post.addHeader(k, String.valueOf(headers.get(k))); + } + + try { + // 默认类型application/x-www-form-urlencoded + if (contentType == null) { + contentType = "application/x-www-form-urlencoded"; + } + + // 兼容普通post和json/xml post + if (contentType.equals("application/x-www-form-urlencoded")) { + response = HttpClientUtils.post(post, params); + }else { + //其他的全部postBody + response = HttpClientUtils.postBody(post, request.getString("params"), encoding); + } + } catch (Exception e) { + response = new Response(); + response.setStatusCode(500); + response.setStatusMessage("本地服务器报错:" + e.getMessage()); + } + + } else if ("GET".equals(method.toUpperCase())) { + HttpGet get = new HttpGet(reqUrl); + for (String k : keys) { + get.addHeader(k, String.valueOf(headers.get(k))); + } + try { + response = HttpClientUtils.get(get); + } catch (Exception e) { + response = new Response(); + response.setStatusCode(500); + response.setStatusMessage("本地服务器报错:" + e.getMessage()); + } + } else { + // 提示请求不支持 + response = new Response(); + response.setStatusCode(500); + response.setEncoding("utf-8"); + response.setStatusMessage(method + "请求类型暂时不支持!"); + } + org.json.JSONObject jsonObject = new org.json.JSONObject(); + try { + + // 处理重定向 + if (response.getStatusCode() == 302 || response.getStatusCode() == 307 || response.getStatusCode() == 303) { + // 处理地址 + String localtion = response.getHeaders().get("Location"); + localtion.replace(server, domain); + response.getHeaders().put("Location", localtion); + } + + byte[] bytes = (byte[]) response.getBody(); + long length = bytes.length; + this.traffic += length; + this.speed += length; + jsonObject.put("body", response.getBody()); + jsonObject.put("headers", response.getHeaders()); + jsonObject.put("statusCode", response.getStatusCode()); + jsonObject.put("encoding", response.getEncoding()); + jsonObject.put("statusMessage", response.getStatusMessage()); + jsonObject.put("eventName", eventName); + } catch (JSONException e) { + e.printStackTrace(); + } + logger.info("请求响应:" + response); + if (callListener != null) { + callListener.eventCall("[目标响应]:" + response); + } + if (socket == null) { + callListener.eventCall("[服务关闭] The service has been closed."); + return; + } + socket.emit(HTTP_RESPONSE, jsonObject); + // 通知界面显示流量 + callListener.trafficCall(this.traffic); + } + + public void stop() { + if (socket != null) { + socket.close(); + socket = null; + } + callListener.ping(0l); + } + + public static void main(String[] args) throws Exception { + new MainForm().setVisible(true); + + } +} diff --git a/wezoz-nat-client/src/main/java/com/wezoz/nat/Response.java b/wezoz-nat-client/src/main/java/com/wezoz/nat/Response.java new file mode 100644 index 0000000..ab9d1be --- /dev/null +++ b/wezoz-nat-client/src/main/java/com/wezoz/nat/Response.java @@ -0,0 +1,65 @@ +package com.wezoz.nat; + +import java.io.Serializable; +import java.util.Map; + +public class Response implements Serializable{ + + private static final long serialVersionUID = 5431808741731247591L; + + private int statusCode = 200;// 状态码 + + private String statusMessage = "ok";// 状态消息 + + private Map headers;// 请求头 + + private String encoding;// 编码 + + private Object body;// 响应内容 + + public int getStatusCode() { + return statusCode; + } + + public void setStatusCode(int statusCode) { + this.statusCode = statusCode; + } + + public String getStatusMessage() { + return statusMessage; + } + + public void setStatusMessage(String statusMessage) { + this.statusMessage = statusMessage; + } + + public Map getHeaders() { + return headers; + } + + public void setHeaders(Map headers) { + this.headers = headers; + } + + public String getEncoding() { + return encoding; + } + + public void setEncoding(String encoding) { + this.encoding = encoding; + } + + public Object getBody() { + return body; + } + + public void setBody(Object body) { + this.body = body; + } + + @Override + public String toString() { + return "Response [statusCode=" + statusCode + ", statusMessage=" + statusMessage + ", headers=" + headers + ", encoding=" + encoding + ", body={}]"; + } + +} diff --git a/wezoz-nat-client/src/main/java/com/wezoz/nat/SocketListener.java b/wezoz-nat-client/src/main/java/com/wezoz/nat/SocketListener.java new file mode 100644 index 0000000..8d56c84 --- /dev/null +++ b/wezoz-nat-client/src/main/java/com/wezoz/nat/SocketListener.java @@ -0,0 +1,40 @@ +package com.wezoz.nat; + +import io.socket.emitter.Emitter.Listener; + +public abstract class SocketListener implements Listener { + + private String eventName; + + private String message; + + public void setMessage(String message) { + this.message = message; + } + + public String getMessage() { + return message; + } + + public void setEventName(String eventName) { + this.eventName = eventName; + } + + public String getEventName() { + return eventName; + } + + @Override + public void call(Object... args) { + this.eventCall(this.getEventName(),this.getMessage(), args); + } + + /** + * 回调事件 + * + * @param eventName + * @param args + */ + public abstract void eventCall(String eventName,String message, Object... args); + +} diff --git a/wezoz-nat-client/src/main/java/com/wezoz/nat/form/BaseForm.java b/wezoz-nat-client/src/main/java/com/wezoz/nat/form/BaseForm.java new file mode 100644 index 0000000..0b229e3 --- /dev/null +++ b/wezoz-nat-client/src/main/java/com/wezoz/nat/form/BaseForm.java @@ -0,0 +1,29 @@ +package com.wezoz.nat.form; + +import java.awt.Color; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; + +import javax.swing.ImageIcon; +import javax.swing.JFrame; + +public class BaseForm extends JFrame { + + private static final long serialVersionUID = -8446051052250946428L; + + public BaseForm() { + getContentPane().setLayout(null); + getContentPane().setBackground(new Color(248, 251, 253)); + this.addWindowListener(new WindowAdapter() { + + @Override + public void windowClosing(WindowEvent e) { + System.exit(0); + super.windowClosing(e); + } + }); + ImageIcon imageIcon = new ImageIcon(getClass().getResource("/logo.png")); + + this.setIconImage(imageIcon.getImage()); + } +} diff --git a/wezoz-nat-client/src/main/java/com/wezoz/nat/form/LoginForm.java b/wezoz-nat-client/src/main/java/com/wezoz/nat/form/LoginForm.java new file mode 100644 index 0000000..e01d74e --- /dev/null +++ b/wezoz-nat-client/src/main/java/com/wezoz/nat/form/LoginForm.java @@ -0,0 +1,80 @@ +package com.wezoz.nat.form; + +import java.awt.Color; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.io.IOException; +import java.net.URI; + +import javax.swing.JButton; +import javax.swing.JTextField; + +public class LoginForm extends BaseForm { + + private static final long serialVersionUID = -8577615925651575124L; + + private JTextField txtUsername; + + private JTextField txtPassword; + + public LoginForm() { + setResizable(false); + + + txtUsername = new JTextField(); + txtUsername.setForeground(new Color(104, 104, 104)); + txtUsername.setBounds(69, 66, 235, 41); + + getContentPane().add(txtUsername); + txtUsername.setColumns(10); + + txtPassword = new JTextField(); + txtPassword.setToolTipText("密码"); + txtPassword.setColumns(10); + txtPassword.setForeground(new Color(104, 104, 104)); + txtPassword.setBounds(69, 122, 235, 41); + getContentPane().add(txtPassword); + + JButton btnLogin = new JButton("登录"); + btnLogin.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + new MainForm().setVisible(true); + } + }); + btnLogin.setBounds(187, 185, 117, 47); + getContentPane().add(btnLogin); + + JButton btnReg = new JButton("注册"); + btnReg.addActionListener(new ActionListener() { + + public void actionPerformed(ActionEvent e) { + if (java.awt.Desktop.isDesktopSupported()) { + try { + // 创建一个URI实例 + URI uri = URI.create("https://www.wezoz.com/register/"); + // 获取当前系统桌面扩展 + java.awt.Desktop dp = java.awt.Desktop.getDesktop(); + // 判断系统桌面是否支持要执行的功能 + if (dp.isSupported(java.awt.Desktop.Action.BROWSE)) { + // 获取系统默认浏览器打开链接 + dp.browse(uri); + } + } catch (NullPointerException e1) { + // 此为uri为空时抛出异常 + } catch (IOException e2) { + // 此为无法获取系统默认浏览器 + } + } + } + }); + btnReg.setBounds(69, 185, 117, 47); + getContentPane().add(btnReg); + this.setSize(400, 300); + this.setLocationRelativeTo(null); + this.setTitle("Wezoz NAT 登录"); + } + + public static void main(String[] args) { + new LoginForm().setVisible(true); + } +} diff --git a/wezoz-nat-client/src/main/java/com/wezoz/nat/form/MainForm.java b/wezoz-nat-client/src/main/java/com/wezoz/nat/form/MainForm.java new file mode 100644 index 0000000..86b3847 --- /dev/null +++ b/wezoz-nat-client/src/main/java/com/wezoz/nat/form/MainForm.java @@ -0,0 +1,247 @@ +package com.wezoz.nat.form; + +import java.awt.Color; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.ComponentAdapter; +import java.awt.event.ComponentEvent; +import java.text.DecimalFormat; +import java.text.SimpleDateFormat; +import java.util.Date; + +import javax.swing.JButton; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTextArea; +import javax.swing.JTextField; +import javax.swing.border.EtchedBorder; +import javax.swing.border.TitledBorder; + +import com.wezoz.nat.CallListener; +import com.wezoz.nat.LocalServer; + +public class MainForm extends BaseForm { + + private static final long serialVersionUID = -8577615925651575124L; + + private JTextField txtHost; + + private JTextArea txtConsole; + + private JButton btnAction; + + private JLabel lblStatus; + + private LocalServer server; + + private JPanel panelSetting; + + private JScrollPane panelConsole; + + private JLabel label_1; + + private JTextField txtDomain; + + private JLabel lblwezozcom; + + private JLabel lablTraffic; + + private JLabel label_3; + + private JLabel lblSpeed; + private JLabel lblPing; + + public MainForm() { + setTitle("Wezoz NAT"); + this.setSize(546, 432); + setLocationRelativeTo(null); + + JLabel label = new JLabel("状态:"); + label.setBounds(20, 27, 61, 16); + getContentPane().add(label); + + lblStatus = new JLabel("服务停止"); + lblStatus.setBounds(60, 27, 280, 16); + getContentPane().add(lblStatus); + + btnAction = new JButton("启动服务"); + btnAction.addActionListener(new ActionListener() { + + public void actionPerformed(ActionEvent e) { + if (btnAction.getText().equals("启动服务")) { + try { + String host = txtHost.getText(); + server = new LocalServer(); + server.setServer("http://wezoz.com:3001"); + server.setForward(host); + server.setDomain(txtDomain.getText()); + server.setCallListener(new CallListener() { + + @Override + public void statusCall(String info) { + lblStatus.setText(info); + } + + @Override + public void eventCall(String info) { + txtConsole.append(info + "\n"); + } + + @Override + public void onClose() { + server.stop(); + lblStatus.setText("服务停止"); + btnAction.setText("启动服务"); + } + + @Override + public void trafficCall(long traffic) { + + lablTraffic.setText(formatNumber(traffic)); + } + + @Override + public void speedCall(long speed) { + lblSpeed.setText(formatNumber(speed)+"/s"); + + } + + @Override + public void ping(long ms) { + lblPing.setText(ms+"ms"); + } + }); + server.start(); + btnAction.setText("停止服务"); + } catch (Exception ex) { + lblStatus.setText("启动失败!请检查地址是否正确"); + } + } else { + server.stop(); + lblStatus.setText("服务停止"); + btnAction.setText("启动服务"); + } + } + }); + btnAction.setBounds(339, 22, 117, 29); + getContentPane().add(btnAction); + + panelSetting = new JPanel(); + panelSetting.setBackground(new Color(248, 251, 253)); + panelSetting.setBorder(new TitledBorder(new EtchedBorder(EtchedBorder.LOWERED, null, null), "\u7F51\u7EDC\u53C2\u6570", TitledBorder.LEADING, TitledBorder.TOP, null, new Color(0, 0, 0))); + panelSetting.setBounds(19, 55, 506, 119); + getContentPane().add(panelSetting); + panelSetting.setLayout(null); + + JLabel lblip = new JLabel("转发地址:"); + lblip.setBounds(18, 64, 65, 16); + panelSetting.add(lblip); + + txtHost = new JTextField(); + txtHost.setText("http://127.0.0.1:8080"); + txtHost.setBounds(82, 59, 331, 26); + panelSetting.add(txtHost); + txtHost.setColumns(10); + + label_1 = new JLabel("绑定域名:"); + label_1.setBounds(18, 30, 65, 16); + panelSetting.add(label_1); + + txtDomain = new JTextField(); + txtDomain.setColumns(10); + txtDomain.setBounds(82, 25, 108, 26); + panelSetting.add(txtDomain); + + lblwezozcom = new JLabel(".wezoz.com"); + lblwezozcom.setBounds(189, 30, 90, 16); + panelSetting.add(lblwezozcom); + + JLabel label_2 = new JLabel("流出流量:"); + label_2.setBounds(18, 92, 65, 16); + panelSetting.add(label_2); + + lablTraffic = new JLabel("0KB"); + lablTraffic.setBounds(82, 92, 96, 16); + panelSetting.add(lablTraffic); + + label_3 = new JLabel("速度:"); + label_3.setBounds(162, 92, 39, 16); + panelSetting.add(label_3); + + lblSpeed = new JLabel("0KB"); + lblSpeed.setBounds(199, 92, 73, 16); + panelSetting.add(lblSpeed); + + JLabel label_4 = new JLabel("延迟:"); + label_4.setBounds(305, 92, 39, 16); + panelSetting.add(label_4); + + lblPing = new JLabel("0ms"); + lblPing.setBounds(342, 92, 123, 16); + panelSetting.add(lblPing); + + txtConsole = new JTextArea() { + + /** + * + */ + private static final long serialVersionUID = 8749801166570350982L; + + @Override + public void append(String str) { + this.setCaretPosition(this.getDocument().getLength()); + str = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date()) + " - " + str; + if (this.getText().length() > 100000) { + this.setText(""); + } + super.append(str); + } + }; + txtConsole.setText("准备就绪\n"); + txtConsole.setBounds(19, 181, 437, 129); + panelConsole = new JScrollPane(txtConsole, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS); + panelConsole.setLocation(20, 210); + panelConsole.setSize(431, 150); + + getContentPane().add(panelConsole); + this.addComponentListener(new ComponentAdapter() { + + @Override + public void componentResized(ComponentEvent e) { + change(); + super.componentResized(e); + } + }); + change(); + } + + private String formatNumber(long traffic) { + String text = null; + DecimalFormat format = new DecimalFormat("#.##"); + Double value = traffic * 0.0001221d; + if (value <= 1024) { + text = format.format(value) + "KB"; + } else if (value / 1024 > 1) { + text = format.format((value / 1024)) + "MB"; + } else if (value / 1024 / 1024 > 1) { + text = format.format((value / 1024 / 1024)) + "GB"; + } else if (value / 1024 / 1024 / 1024 > 1) { + text = format.format((value / 1024 / 1024 / 1024)) + "TB"; + } + return text; + } + + private void change() { + int width = MainForm.this.getWidth(); + int height = MainForm.this.getHeight(); + int wv = width - 40; + panelSetting.setSize(wv, panelSetting.getHeight()); + panelConsole.setSize(wv, height - panelConsole.getY() - 40); + btnAction.setLocation(width - 20 - btnAction.getWidth(), btnAction.getY()); + } + + public static void main(String[] args) { + new MainForm().setVisible(true); + } +} diff --git a/wezoz-nat-client/src/main/java/com/wezoz/nat/utils/HttpClientUtils.java b/wezoz-nat-client/src/main/java/com/wezoz/nat/utils/HttpClientUtils.java new file mode 100644 index 0000000..497d4bf --- /dev/null +++ b/wezoz-nat-client/src/main/java/com/wezoz/nat/utils/HttpClientUtils.java @@ -0,0 +1,455 @@ +package com.wezoz.nat.utils; + +import java.io.BufferedInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.net.HttpURLConnection; +import java.net.SocketTimeoutException; +import java.net.URI; +import java.net.URL; +import java.security.GeneralSecurityException; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; +import java.text.ParseException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.zip.GZIPOutputStream; + +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLException; +import javax.net.ssl.SSLSession; +import javax.net.ssl.SSLSocket; + +import org.apache.commons.io.IOUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.http.Header; +import org.apache.http.HttpEntity; +import org.apache.http.client.config.RequestConfig; +import org.apache.http.client.entity.UrlEncodedFormEntity; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.conn.ssl.SSLConnectionSocketFactory; +import org.apache.http.conn.ssl.SSLContextBuilder; +import org.apache.http.conn.ssl.TrustStrategy; +import org.apache.http.conn.ssl.X509HostnameVerifier; +import org.apache.http.entity.ByteArrayEntity; +import org.apache.http.entity.StringEntity; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClients; +import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; +import org.apache.http.message.BasicNameValuePair; +import org.apache.http.util.EntityUtils; + +import com.alibaba.fastjson.JSON; +import com.wezoz.nat.Response; + +/** + * http请求工具 httpclient4.5 + * + * @author panjing + * @project wootop-common + * @date 2016年4月19日 上午11:20:43 + */ +@SuppressWarnings("deprecation") +public class HttpClientUtils { + + private static Log log = LogFactory.getLog(HttpClientUtils.class); + + + public final static String DEFAULT_ENCODING = "UTF-8"; + + public final static String CONTENT_TYPE = "Content-Type"; + + public final static String TEXT_HTML = "text/html"; + + public final static int STATUS_CODE_SUCCESS = 200; + + private static CloseableHttpClient httpClient; + + private static PoolingHttpClientConnectionManager connMgr; + + private static RequestConfig requestConfig; + + private static final int MAX_TIMEOUT = 7000; + + static { + // 设置连接池 + connMgr = new PoolingHttpClientConnectionManager(); + // 设置连接池大小 + connMgr.setMaxTotal(100); + connMgr.setDefaultMaxPerRoute(connMgr.getMaxTotal()); + + RequestConfig.Builder configBuilder = RequestConfig.custom(); + // 设置连接超时 + configBuilder.setConnectTimeout(MAX_TIMEOUT); + // 设置读取超时 + configBuilder.setSocketTimeout(MAX_TIMEOUT); + // 设置从连接池获取连接实例的超时 + configBuilder.setConnectionRequestTimeout(MAX_TIMEOUT); + // 在提交请求之前 测试连接是否可用 + configBuilder.setStaleConnectionCheckEnabled(true); + requestConfig = configBuilder.build(); + } + + public static CloseableHttpClient getClient() { + + if (httpClient == null) { + //不重定向处理 + RequestConfig config = RequestConfig.custom().setConnectTimeout(100000).setConnectionRequestTimeout(100000).setSocketTimeout(100000).setRedirectsEnabled(false).build(); + + httpClient = HttpClients.custom().setDefaultRequestConfig(config).build(); + + } + return httpClient; + } + + /** + * get请求 + * + * @param url + * @return + */ + public static Response get(String url) { + + HttpGet httpGet = new HttpGet(url); + return get(httpGet); + } + + /** + * get请求 + * + * @param httpGet + * @return + */ + public static Response get(HttpGet httpGet) { + Response response = new Response(); + try { + CloseableHttpResponse res = getClient().execute(httpGet); + response = getContent(res); + + } catch (SocketTimeoutException e) { + log.error("Read timed out:" + httpGet.getURI()); + response.setStatusCode(502); + response.setStatusMessage("读取超时"); + } catch (Exception e) { + response.setStatusCode(500); + response.setStatusMessage("请求本地服务器出错"); + log.error(e); + } + return response; + } + + /** + * post请求 + * + * @param url + * @param params + * @return + */ + public static Response post(String url, Map params) { + + HttpPost httpPost = new HttpPost(url); + + return post(httpPost, params); + } + + public static Response postSSL(String url, Map params) { + + HttpPost httpPost = new HttpPost(url); + return postSSL(httpPost, params); + } + + /** + * post请求 + * + * @param httpPost + * @param params + * @return + */ + public static Response post(HttpPost httpPost, Map params) { + Response res = null; + try { + if (params != null) { + List basicNameValuePairs = new ArrayList(); + + Set keySet = params.keySet(); + for (String key : keySet) { + Object obj = params.get(key); + String value = null; + if (obj != null) { + value = String.valueOf(obj); + } + basicNameValuePairs.add(new BasicNameValuePair(key, value)); + } + + HttpEntity httpEntity = new UrlEncodedFormEntity(basicNameValuePairs, DEFAULT_ENCODING); + + httpPost.setEntity(httpEntity); + } + CloseableHttpResponse response = getClient().execute(httpPost); + res = getContent(response); + } catch (Exception e) { + e.printStackTrace(); + log.error(e); + } + return res; + } + + public static Response postBody(HttpPost httpPost, String body,String encoding) { + Response res = null; + try { + httpPost.setEntity(new StringEntity(body, encoding)); + CloseableHttpResponse response = getClient().execute(httpPost); + res = getContent(response); + } catch (Exception e) { + e.printStackTrace(); + log.error(e); + } + return res; + } + + public static Response gzipPost(String url, Map params) { + HttpPost httpPost = new HttpPost(url); + return gzipPost(httpPost, params); + } + + public static Response gzipPost(HttpPost httpPost, Map params) { + + Response res = null; + try { +// httpPost.addHeader("User-Agent", DEFAULT_USER_AGENT); + httpPost.addHeader("Content-Encoding", "gzip"); + if (params != null) { + List basicNameValuePairs = new ArrayList(); + + Set keySet = params.keySet(); + for (String key : keySet) { + Object obj = params.get(key); + String value = null; + if (obj != null) { + value = String.valueOf(obj); + } + basicNameValuePairs.add(new BasicNameValuePair(key, value)); + } + + // HttpEntity httpEntity = new + // UrlEncodedFormEntity(basicNameValuePairs, DEFAULT_ENCODING); + String str = JSON.toJSONString(params); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + GZIPOutputStream gos = new GZIPOutputStream(baos); + gos.write(str.getBytes("utf-8")); + gos.flush(); + + byte[] bs = baos.toByteArray(); + HttpEntity httpEntity = new ByteArrayEntity(bs); + + httpPost.setEntity(httpEntity); + } + CloseableHttpResponse response = getClient().execute(httpPost); + res = getContent(response); + } catch (Exception e) { + e.printStackTrace(); + log.error(e); + } + return res; + } + + public static Response postSSL(HttpPost httpPost, Map params) { + + Response res = null; + try { +// httpPost.addHeader("User-Agent", DEFAULT_USER_AGENT); + List basicNameValuePairs = new ArrayList(); + + Set keySet = params.keySet(); + for (String key : keySet) { + Object obj = params.get(key); + String value = null; + if (obj != null) { + value = String.valueOf(obj); + } + basicNameValuePairs.add(new BasicNameValuePair(key, value)); + } + HttpEntity httpEntity = new UrlEncodedFormEntity(basicNameValuePairs, DEFAULT_ENCODING); + + httpPost.setEntity(httpEntity); + URI uri = httpPost.getURI(); + new SSLHttpClient().registerSSL(uri.getHost(), "https", uri.getPort(), uri.getScheme()); + CloseableHttpClient httpClient = HttpClients.custom().setSSLSocketFactory(createSSLConnSocketFactory()).setConnectionManager(connMgr).setDefaultRequestConfig(requestConfig).build(); + CloseableHttpResponse response = httpClient.execute(httpPost); + res = getContent(response); + } catch (Exception e) { + e.printStackTrace(); + log.error(e); + } + return res; + } + + private static SSLConnectionSocketFactory createSSLConnSocketFactory() { + + SSLConnectionSocketFactory sslsf = null; + try { + SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(null, new TrustStrategy() { + + public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException { + + return true; + } + }).build(); + sslsf = new SSLConnectionSocketFactory(sslContext, new X509HostnameVerifier() { + + @Override + public boolean verify(String arg0, SSLSession arg1) { + + return true; + } + + @Override + public void verify(String host, SSLSocket ssl) throws IOException { + + } + + @Override + public void verify(String host, X509Certificate cert) throws SSLException { + + } + + @Override + public void verify(String host, String[] cns, String[] subjectAlts) throws SSLException { + + } + }); + } catch (GeneralSecurityException e) { + e.printStackTrace(); + } + return sslsf; + } + + // 功能: postBody形式发送数据 + // @param urlPath 对方地址 + // @param json 要传送的数据 + // @return + // @throws Exception + public static String postBody(String urlPath, String data) throws Exception { + + // Configure and open a connection to the site you will send the + // request + URL url = new URL(urlPath); + HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection(); + // 设置doOutput属性为true表示将使用此urlConnection写入数据 + urlConnection.setDoOutput(true); + // 定义待写入数据的内容类型,我们设置为application/x-www-form-urlencoded类型 + urlConnection.setRequestProperty("content-type", "application/x-www-form-urlencoded"); + // 得到请求的输出流对象 + OutputStreamWriter out = new OutputStreamWriter(urlConnection.getOutputStream()); + // 把数据写入请求的Body + out.write(data); + out.flush(); + out.close(); + // 从服务器读取响应 + InputStream inputStream = urlConnection.getInputStream(); + String encoding = urlConnection.getContentEncoding(); + String body = IOUtils.toString(inputStream, encoding); + if (urlConnection.getResponseCode() != 200) { + throw new Exception(body); + } + + return body; + } + + public static String getEncoding(CloseableHttpResponse response) { + + String encoding = null; + + Header[] headers = response.getHeaders(CONTENT_TYPE); + if (headers != null && headers.length > 0) { + Header header = headers[0]; + String value = header.getValue(); + value = value.replaceAll(" ", ""); + value = value.toLowerCase(); + int index = 0; + if ((index = value.lastIndexOf("charset")) != -1) { + String[] array = value.substring(index).split("="); + if (array.length > 1) { + encoding = array[1]; + } + } + } + + return encoding; + } + + /** + * 获取响应内容 + * + * @param response + * @return + * @throws ParseException + * @throws IOException + */ + public static Response getContent(CloseableHttpResponse response) throws ParseException, IOException { + + Response res = new Response(); + HttpEntity entity = response.getEntity(); + if (entity == null) { + return res; + } + + byte[] bytes = readBytes(entity.getContent()); + String encoding = getEncoding(response); + if (encoding == null) { + encoding = "utf-8"; + } + res.setBody(bytes); + res.setEncoding(encoding); + res.setStatusCode(response.getStatusLine().getStatusCode()); + + Map headersMap = new HashMap(); + Header[] headers = response.getAllHeaders(); + for (Header header : headers) { + headersMap.put(header.getName(), header.getValue()); + } + res.setHeaders(headersMap); + + // content = new String(bytes, encoding); + EntityUtils.consume(response.getEntity()); + response.close(); + + return res; + } + + /** + * 将流读取为字节数组 + * + * @param in + * @return + * @throws IOException + */ + public static byte[] readBytes(InputStream in) throws IOException { + + BufferedInputStream bufin = new BufferedInputStream(in); + int buffSize = 1024; + ByteArrayOutputStream out = new ByteArrayOutputStream(buffSize); + + byte[] temp = new byte[buffSize]; + int size = 0; + while ((size = bufin.read(temp)) != -1) { + out.write(temp, 0, size); + } + bufin.close(); + + byte[] content = out.toByteArray(); + return content; + } + + public static void main(String[] args) { + System.out.println(get("http://localhost:8080/wootop-doctor/")); + } +} diff --git a/wezoz-nat-client/src/main/java/com/wezoz/nat/utils/SSLHttpClient.java b/wezoz-nat-client/src/main/java/com/wezoz/nat/utils/SSLHttpClient.java new file mode 100644 index 0000000..3c48463 --- /dev/null +++ b/wezoz-nat-client/src/main/java/com/wezoz/nat/utils/SSLHttpClient.java @@ -0,0 +1,89 @@ +package com.wezoz.nat.utils; + +import java.security.KeyManagementException; +import java.security.NoSuchAlgorithmException; +import java.security.Principal; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; + +import javax.net.ssl.SSLContext; +import javax.net.ssl.TrustManager; +import javax.net.ssl.X509TrustManager; + +import org.apache.http.conn.scheme.Scheme; +import org.apache.http.conn.ssl.SSLSocketFactory; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClients; + +@SuppressWarnings("deprecation") +public class SSLHttpClient { + /** + * 注册SSL连接 + * @param hostname 请求的主机名(IP或者域名) + * @param protocol 请求协议名称(TLS-安全传输层协议) + * @param port 端口号 + * @param scheme 协议名称 + * @return HttpClient实例 + * @throws NoSuchAlgorithmException + * @throws KeyManagementException + */ + public CloseableHttpClient registerSSL(String hostname,String protocol,int port,String scheme)throws NoSuchAlgorithmException, KeyManagementException { + //创建一个默认的HttpClient + CloseableHttpClient httpclient = HttpClients.createDefault(); + //创建SSL上下文实例 + SSLContext ctx = SSLContext.getInstance(protocol); + //服务端证书验证 + X509TrustManager tm = new X509TrustManager() { + /** + * 验证客户端证书 + */ + @Override + public void checkClientTrusted(X509Certificate[] chain,String authType) + throws java.security.cert.CertificateException { + //这里跳过客户端证书 验证 + } + + /** + * 验证服务端证书 + * @param chain 证书链 + * @param authType 使用的密钥交换算法,当使用来自服务器的密钥时authType为RSA + */ + @Override + public void checkServerTrusted(X509Certificate[] chain,String authType) + throws java.security.cert.CertificateException { + if (chain == null || chain.length == 0) + throw new IllegalArgumentException("null or zero-length certificate chain"); + if (authType == null || authType.length() == 0) + throw new IllegalArgumentException("null or zero-length authentication type"); + + boolean br = false; + Principal principal = null; + for (X509Certificate x509Certificate : chain) { + principal = x509Certificate.getSubjectX500Principal(); + if (principal != null) { + br = true; + return; + } + } + if (!br) { + throw new CertificateException("服务端证书验证失败!"); + } + } + /** + * 返回CA发行的证书 + */ + @Override + public X509Certificate[] getAcceptedIssuers() { + return new X509Certificate[0]; + } + }; + //初始化SSL上下文 + ctx.init(null, new TrustManager[]{tm}, new java.security.SecureRandom()); + //创建SSL连接 + SSLSocketFactory socketFactory = new SSLSocketFactory(ctx,SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER); + Scheme sch = new Scheme(scheme, port, socketFactory); + //注册SSL连接 + httpclient.getConnectionManager().getSchemeRegistry().register(sch); + return httpclient; + } +} diff --git a/wezoz-nat-client/src/main/resources/log4j.properties b/wezoz-nat-client/src/main/resources/log4j.properties new file mode 100755 index 0000000..0646c93 --- /dev/null +++ b/wezoz-nat-client/src/main/resources/log4j.properties @@ -0,0 +1,23 @@ +log4j.rootLogger=INFO,ERROR + +log4j.appender=org.apache.log4j.RollingFileAppender +log4j.appender.File=wezoz-nat.log +log4j.appender.MaxFileSize=10MB +log4j.appender.Threshold=ALL +log4j.appender.layout=org.apache.log4j.PatternLayout +log4j.appender.layout.ConversionPattern=-%t %d{yyyy-MM-dd HH\:mm\:ss,SSS} -%p -%l -%m %n +log4j.appender.BufferSize=8192 + + +log4j.appender.INFO=org.apache.log4j.ConsoleAppender +log4j.appender.INFO.Target=System.out +log4j.appender.INFO.layout=org.apache.log4j.PatternLayout +log4j.appender.INFO.layout.ConversionPattern=-%t %d{yyyy-MM-dd HH\:mm\:ss,SSS} -%p -%l -%m %n + +log4j.appender.ERROR=org.apache.log4j.ConsoleAppender +log4j.appender.ERROR.layout=org.apache.log4j.PatternLayout +log4j.appender.ERROR.Target=System.out +log4j.appender.ERROR.layout=org.apache.log4j.PatternLayout +log4j.appender.ERROR.layout.ConversionPattern=-%t %d{yyyy-MM-dd HH\:mm\:ss,SSS} -%p -%l -%m %n + +#log4j.logger.org.apache.http=OFF \ No newline at end of file diff --git a/wezoz-nat-client/src/main/resources/logo.png b/wezoz-nat-client/src/main/resources/logo.png new file mode 100644 index 0000000..ee3cae4 Binary files /dev/null and b/wezoz-nat-client/src/main/resources/logo.png differ