urlSet = urlJsonIgnorePath.keySet();
225 |
226 | for (int i = 0; i < leftList.size(); i++) {
227 | ReqRespEntity leftEntity = leftList.get(i);
228 | String leftUrl = leftEntity.getRequestUrl().toString();
229 |
230 | ReqRespEntity rightEntity = null;
231 |
232 | for (int j = 0; j < rightList.size(); j++) {
233 | if (leftUrl.equals(rightList.get(j).getRequestUrl().toString())) {
234 | rightEntity = rightList.get(j);
235 | break;
236 | }
237 | }
238 | if (rightEntity == null) {
239 | continue;
240 | }
241 |
242 | String rightUrl = rightEntity.getRequestUrl().toString();
243 |
244 | /**
245 | * 获取配置文件中与当前URL匹配的配置项
246 | */
247 | String jsonIgnorePath = null;
248 | for (String urlRegular : urlSet) {
249 | if (leftUrl.matches(urlRegular)) {
250 | jsonIgnorePath = urlJsonIgnorePath.get(urlRegular);
251 | break;
252 | }
253 | }
254 |
255 | String leftResponseContent = leftEntity.getResponseContent().toString();
256 | String rightResponseContent = rightEntity.getResponseContent().toString();
257 |
258 | /**
259 | * 将响应报文比较后不同的响应内容记录到结果文件
260 | */
261 | if (!isContentSame(leftResponseContent, rightResponseContent, leftEntity.isResponseJson(), jsonIgnorePath)) {
262 | append("===================================");
263 | append(leftUrl);
264 | append("");
265 | append(rightUrl);
266 | append("");
267 | append("left 响应内容 :");
268 | append(leftEntity.getResponseContent().toString());
269 | append("");
270 | append("right 响应内容 :");
271 | append(rightEntity.getResponseContent().toString());
272 | append("===================================");
273 | append("");
274 | append("");
275 | append("");
276 | }
277 | }
278 | }
279 |
280 | public static void main(String[] args) throws Exception {
281 | CompareHttpLog compareHttpLog = new CompareHttpLog("/Users/niwei/Downloads/gor-test-2016-07-22-19.log",
282 | "/Users/niwei/Downloads/gor-online-2016-07-22-19.log",
283 | "/Users/niwei/Downloads/compare-2016-07-22.log",
284 | "go/urlJsonIgnorePath.properties");
285 |
286 | compareHttpLog.compare();
287 |
288 | }
289 | }
290 |
--------------------------------------------------------------------------------
/src/go/JsonContentCompare.java:
--------------------------------------------------------------------------------
1 | package go;
2 |
3 | import org.json.JSONArray;
4 | import org.json.JSONObject;
5 |
6 | import java.util.*;
7 |
8 | /**
9 | * json内容比较工具类
10 | *
11 | * Created by niwei on 16/7/21.
12 | */
13 | public class JsonContentCompare {
14 | private static final String PATH_HEAD = "JSONHEAD";//json路径头定义
15 | private static final String PATH_SPLITTER = "#";//json路径分隔符定义,选择正则表达式中不用的,避免解析正则表达式的时候处理麻烦
16 |
17 | private String json;//原始json字符串
18 | private String compareJson;//被比较的json字符串
19 | private List jsonPathList;//保存原始json的多有解析过的value的路径
20 | private Set ignorePahtSet;//比较路径黑名单,用于忽略某些路径值的比较
21 |
22 | public JsonContentCompare(String json, String compareJson, Set ignorePahtSet) {
23 | this.json = json;
24 | this.compareJson = compareJson;
25 | this.jsonPathList = new ArrayList();
26 | if (ignorePahtSet == null) {
27 | ignorePahtSet = new HashSet<>();
28 | }
29 | this.ignorePahtSet = ignorePahtSet;
30 | }
31 |
32 | public boolean compare() {
33 | boolean result = false;
34 |
35 | if (json == null || json.trim().equals("") || compareJson == null || compareJson.trim().equals("")) {
36 | return result;
37 | }
38 |
39 | JSONObject jsonObj = new JSONObject(json);
40 | JSONObject compareJsonObj = new JSONObject(compareJson);
41 | String jsonPath = PATH_HEAD;
42 | if ((jsonObj != JSONObject.NULL) && (compareJsonObj != JSONObject.NULL)) {
43 | result = compareObject(jsonObj, compareJsonObj, jsonPath);
44 | }
45 |
46 | return result;
47 | }
48 |
49 | /**
50 | * json的路径是否包含在ignorePahtSet中
51 | *
52 | * @param jsonPath
53 | * @return
54 | */
55 | private boolean isInIgnorePathJudge(String jsonPath) {
56 | boolean result = false;
57 | for (String path : ignorePahtSet) {
58 | if (jsonPath.matches(path)) {
59 | result = true;
60 | break;
61 | }
62 | }
63 | return result;
64 | }
65 |
66 | /**
67 | * json对象比较
68 | *
69 | * @param jsonObj json对象
70 | * @param compareJsonObj 与json对象比较的json对象
71 | * @param jsonPath json对象在原始json中所处的路径
72 | * @return 比较结果
73 | */
74 | private boolean compareObject(JSONObject jsonObj, JSONObject compareJsonObj, String jsonPath) {
75 | boolean result = true;
76 |
77 | Iterator jsonKeys = jsonObj.keys();
78 | String parentPath = jsonPath;
79 | while (jsonKeys.hasNext()) {
80 | String jsonKey = jsonKeys.next();
81 | jsonPath = parentPath + PATH_SPLITTER + jsonKey;
82 | if (isInIgnorePathJudge(jsonPath)) {
83 | //如果包含在ignorePahtSet中则忽略本次比较
84 | continue;
85 | }
86 |
87 | if (!compareJsonObj.has(jsonKey)) {
88 | //如果被比较的json中没有该key直接返回false
89 | result = false;
90 | break;
91 | }
92 | Object compareJsonValue = compareJsonObj.get(jsonKey);
93 | Object jsonValue = jsonObj.get(jsonKey);
94 |
95 | //json的value通用比较方法
96 | result = compareJsonValue(jsonValue, compareJsonValue, jsonPath);
97 |
98 | if (!result) {
99 | //result等于false,直接跳出循环,不再继续比较
100 | break;
101 | }
102 | }
103 |
104 | return result;
105 | }
106 |
107 | /**
108 | * json数组比较
109 | *
110 | * @param jsonArr json数组
111 | * @param compareJsonArr 与json数组比较的json数组
112 | * @param jsonPath json数组在原始json中所处的路径
113 | * @return 比较结果
114 | */
115 | private boolean compareArray(JSONArray jsonArr, JSONArray compareJsonArr, String jsonPath) {
116 | boolean result = true;
117 |
118 | String parentPath = jsonPath;
119 | for (int i = 0; i < jsonArr.length(); i++) {
120 | jsonPath = parentPath + "[" + i + "]";
121 |
122 | Object jsonValue = jsonArr.get(i);
123 | Object compareJsonValue = compareJsonArr.get(i);
124 | result = compareJsonValue(jsonValue, compareJsonValue, jsonPath);
125 |
126 | if (!result) {
127 | //result等于false,直接跳出循环,不再继续比较
128 | break;
129 | }
130 | }
131 | return result;
132 | }
133 |
134 | /**
135 | * 普通json值比较
136 | *
137 | * @param jsonValue json值
138 | * @param compareJsonValue 与json值比较的json值
139 | * @param jsonPath json值在原始json中所处的路径
140 | * @return
141 | */
142 | private boolean comparePlain(Object jsonValue, Object compareJsonValue, String jsonPath) {
143 | boolean result = true;
144 |
145 | if (isInIgnorePathJudge(jsonPath)) {
146 | return result;
147 | }
148 |
149 | // 直接比较字符串内容
150 | if (jsonValue != null && !jsonValue.equals(compareJsonValue)) {
151 | result = false;
152 | }
153 | return result;
154 | }
155 |
156 | /**
157 | * 比较json的value,需要根据实际json类型执行不同的解析路径
158 | *
159 | * @param jsonValue 原始json解析后的value
160 | * @param compareJsonValue 原始被比较json解析后的value
161 | * @param jsonPath jsonValue在原始json中所处的路径
162 | * @return
163 | */
164 | private boolean compareJsonValue(Object jsonValue, Object compareJsonValue, String jsonPath) {
165 | boolean result = true;
166 |
167 | if (isInIgnorePathJudge(jsonPath)) {
168 | return result;
169 | }
170 |
171 | if (jsonValue instanceof JSONArray) {
172 | if (!(compareJsonValue instanceof JSONArray)) {
173 | //如果两个json对象类型不一样,直接返回false
174 | result = false;
175 | } else {
176 | result = compareArray((JSONArray) jsonValue, (JSONArray) compareJsonValue, jsonPath);
177 | }
178 | } else if (jsonValue instanceof JSONObject) {
179 | if (!(compareJsonValue instanceof JSONObject)) {
180 | //如果两个json对象类型不一样,直接返回false
181 | result = false;
182 | } else {
183 | result = compareObject((JSONObject) jsonValue, (JSONObject) compareJsonValue, jsonPath);
184 | }
185 | } else {
186 | result = comparePlain(jsonValue, compareJsonValue, jsonPath);
187 | }
188 | this.jsonPathList.add(jsonPath);
189 | return result;
190 | }
191 |
192 | public List getJsonPathList() {
193 | return jsonPathList;
194 | }
195 |
196 | public static void main(String[] args) throws Exception {
197 | String jsonStr1 = "{\"result\":\"ok\",\"msg\":\"操作成功\",\"code\":200,\"data\":{\"LOCATION_THIRD\":{\"promotionId\":0,\"linkUrl\":\"\",\"promotionType\":null,\"showImgUrl\":\"http://static2.8dol.com/homeAds/TOP/618zq.jpg\",\"homeShowType\":\"INVITE\",\"typeName\":\"邀请好友\",\"timeDiff\":0,\"secondKillType\":null},\"LOCATION_FIRST\":{\"promotionId\":102,\"linkUrl\":\"http://t.cn/RqFwUhW\",\"promotionType\":\"SECOND_KILL\",\"showImgUrl\":\"http://static2.8dol.com/homeAds/TOP/622ms.jpg\",\"homeShowType\":\"WEB_SITE\",\"typeName\":\"秒杀\",\"timeDiff\":26632127,\"secondKillType\":\"END\"},\"LOCATION_SECOND\":{\"promotionId\":6,\"linkUrl\":\"\",\"promotionType\":\"FULL_DISCOUNT\",\"showImgUrl\":\"http://static2.8dol.com/homeAds/TOP/622tg.jpg\",\"homeShowType\":\"PROMOTION\",\"typeName\":\"团购批发\",\"timeDiff\":0,\"secondKillType\":null}},\"rescode\":200}";
198 |
199 | String jsonStr2 = "{\"result\":\"ok\",\"msg\":\"操作成功\",\"code\":200,\"data\":{\"LOCATION_SECOND\":{\"promotionId\":102,\"linkUrl\":\"http://t.cn/RqFwUhW\",\"promotionType\":\"SECOND_KILL\",\"showImgUrl\":\"http://static2.8dol.com/homeAds/secondkill.jpg\",\"homeShowType\":\"PROMOTION\",\"typeName\":\"秒杀\",\"timeDiff\":26631850,\"secondKillType\":\"END\"},\"LOCATION_FIRST\":{\"promotionId\":0,\"linkUrl\":\"\",\"promotionType\":null,\"showImgUrl\":\"http://static2.8dol.com/homeAds/first.jpg\",\"homeShowType\":\"DAY\",\"typeName\":\"默认广告位1显示\",\"timeDiff\":0,\"secondKillType\":null},\"LOCATION_THIRD\":{\"promotionId\":0,\"linkUrl\":\"\",\"promotionType\":null,\"showImgUrl\":\"http://static2.8dol.com/homeAds/third.jpg\",\"homeShowType\":\"INVITE\",\"typeName\":\"默认广告位2显示\",\"timeDiff\":0,\"secondKillType\":null}},\"rescode\":200}";
200 |
201 | Set blackPathSet = new HashSet<>();
202 | blackPathSet.add(PATH_HEAD + PATH_SPLITTER + ".*LOCATION_SECOND.*");
203 | blackPathSet.add(PATH_HEAD + PATH_SPLITTER + ".*LOCATION_THIRD.*");
204 | blackPathSet.add(PATH_HEAD + PATH_SPLITTER + ".*LOCATION_FIRST.*");
205 |
206 | JsonContentCompare jsonContentCompare = new JsonContentCompare(jsonStr1, jsonStr2, blackPathSet);
207 |
208 | System.out.println("对象比较结果:" + jsonContentCompare.compare());
209 |
210 | System.out.println("对象比较路径:");
211 |
212 | jsonContentCompare.getJsonPathList().forEach(path -> {
213 | System.out.println(path);
214 | });
215 |
216 | /*String jsonStr1 = "{\"result\":\"ok\",\"msg\":\"登录成功\",\"code\":200,\"data\":{\"username\":\"18625150155\",\"mobile\":\"18625150155\",\"email\":\"\",\"status\":1,\"head_ico\":\"\",\"lock_reason\":\"\",\"open_id_app\":\"\",\"union_id\":\"\",\"verify_code\":\"Toalzwd-gppx-CUEL2WFYDLFPKXIZ54URGAR6IM-xo\",\"bind_mobile\":1,\"isBind8Dol\":1,\"user_id\":\"a4b55123100c8fcb733e7ded03465967b58017b3\",\"is_Vip\":false,\"vip_expire_time\":\"\"},\"rescode\":200,\"timestamp\":1469009437228}";
217 |
218 | String jsonStr2 = "{\"result\":\"ok\",\"msg\":\"操作成功\",\"code\":200,\"data\":[],\"rescode\":200}";
219 |
220 | Set blackPathSet = new HashSet<>();
221 | blackPathSet.add(".*#msg");
222 | blackPathSet.add(".*#data");
223 | blackPathSet.add(".*#timestamp");
224 |
225 |
226 | JsonContentCompare jsonContentCompare = new JsonContentCompare(jsonStr1, jsonStr2, blackPathSet);
227 |
228 | System.out.println("对象比较结果:" + jsonContentCompare.compare());
229 |
230 | System.out.println("对象比较路径:");
231 |
232 | jsonContentCompare.getJsonPathList().forEach(path -> {
233 | System.out.println(path);
234 | });*/
235 | }
236 | }
237 |
--------------------------------------------------------------------------------
/src/go/ReqRespEntity.java:
--------------------------------------------------------------------------------
1 | package go;
2 |
3 | /**
4 | * Created by niwei on 16/7/20.
5 | */
6 | public class ReqRespEntity {
7 | private String id;
8 |
9 | //请求URL,包含了GET请求URL和POST请求的URL及请求体
10 | private StringBuilder requestUrl = new StringBuilder();
11 |
12 | private String responseContentType;
13 | private String responseTransferEncoding;
14 | private boolean isResponseJson = false;
15 | private String responseContentLength;
16 | private StringBuilder responseContent = new StringBuilder();
17 |
18 | public String getId() {
19 | return id;
20 | }
21 |
22 | public void setId(String id) {
23 | this.id = id;
24 | }
25 |
26 | public StringBuilder getRequestUrl() {
27 | return requestUrl;
28 | }
29 |
30 | public void setRequestUrl(StringBuilder requestUrl) {
31 | this.requestUrl = requestUrl;
32 | }
33 |
34 | public String getResponseContentType() {
35 | return responseContentType;
36 | }
37 |
38 | public void setResponseContentType(String responseContentType) {
39 | this.responseContentType = responseContentType;
40 | }
41 |
42 | public String getResponseTransferEncoding() {
43 | return responseTransferEncoding;
44 | }
45 |
46 | public void setResponseTransferEncoding(String responseTransferEncoding) {
47 | this.responseTransferEncoding = responseTransferEncoding;
48 | }
49 |
50 | public boolean isResponseJson() {
51 | return isResponseJson;
52 | }
53 |
54 | public void setResponseJson(boolean responseJson) {
55 | isResponseJson = responseJson;
56 | }
57 |
58 | public String getResponseContentLength() {
59 | return responseContentLength;
60 | }
61 |
62 | public void setResponseContentLength(String responseContentLength) {
63 | this.responseContentLength = responseContentLength;
64 | }
65 |
66 | public StringBuilder getResponseContent() {
67 | return responseContent;
68 | }
69 |
70 | public void setResponseContent(StringBuilder responseContent) {
71 | this.responseContent = responseContent;
72 | }
73 |
74 | @Override
75 | public String toString() {
76 | return "ReqRespEntity{" +
77 | "id='" + id + '\'' +
78 | ", requestUrl=" + requestUrl +
79 | ", responseContentType='" + responseContentType + '\'' +
80 | ", responseTransferEncoding='" + responseTransferEncoding + '\'' +
81 | ", isResponseJson=" + isResponseJson +
82 | ", responseContentLength='" + responseContentLength + '\'' +
83 | ", responseContent=" + responseContent +
84 | '}';
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/src/go/middleware/Stdout.java:
--------------------------------------------------------------------------------
1 | package go.middleware;
2 |
3 | import javax.xml.bind.DatatypeConverter;
4 | import java.io.BufferedInputStream;
5 | import java.io.BufferedReader;
6 | import java.io.InputStreamReader;
7 | import java.util.HashSet;
8 | import java.util.Set;
9 |
10 | /**
11 | * Gor中间件Java版本,增强的功能有:
12 | *
13 | * 1.在请求体中注入参数GorRequestId,用于请求回放时的原始请求比对
14 | * 2.支持根据url配置过滤请求和响应的输出
15 | *
16 | * Created by niwei on 16/7/22.
17 | */
18 | public class Stdout {
19 | private static final String SPLITTER_HEADER_BODY_SPLITTER = "\r\n\r\n";
20 | private static final String SPLITTER_HEAD_FIRST_LINE = "\n";
21 | private static final String SPLITTER_HEADER_ITEM = " ";
22 | /**
23 | * payload type, possible values: 1 - request, 2 - original response, 3 - replayed response
24 | */
25 | private static final String PAYLOAD_TYPE_REQUEST = "1";
26 | private static final String PAYLOAD_TYPE_ORIGINAL_RESPONSE = "2";
27 |
28 | /**
29 | * 定义新增加的requestId参数名称
30 | */
31 | private static String INJECT_TO_REQUEST_ENTITY_REQUEST_ID = "GorRequestId";
32 |
33 | /**
34 | * 定义需要输出的请求和响应的requestId
35 | */
36 | private static Set recordRequestIds = new HashSet<>();
37 |
38 | /**
39 | * convert hex to string
40 | *
41 | * @param hexStr
42 | * @return
43 | * @throws Exception
44 | */
45 | public static String hexDecode(String hexStr) throws Exception {
46 | byte[] decodedHex = DatatypeConverter.parseHexBinary(hexStr);
47 | String decodedString = new String(decodedHex, "UTF-8");
48 |
49 | return decodedString;
50 | }
51 |
52 | /**
53 | * convert string to hex
54 | *
55 | * @param str
56 | * @return
57 | * @throws Exception
58 | */
59 | public static String encodeHex(String str) throws Exception {
60 | if (str == null) {
61 | return null;
62 | }
63 | byte[] strBytes = str.getBytes();
64 | String encodeString = DatatypeConverter.printHexBinary(strBytes);
65 |
66 | return encodeString;
67 | }
68 |
69 | private static String getRequestHeader(String key, String value) {
70 | StringBuilder result = new StringBuilder(SPLITTER_HEAD_FIRST_LINE);
71 |
72 | result.append(key).append(":").append(SPLITTER_HEADER_ITEM).append(value);
73 |
74 | return result.toString();
75 | }
76 |
77 | /**
78 | * gor原始内容增强
79 | *
80 | * @param content 原始的gor工具输出的内容
81 | * @param allowUrlRegular 允许记录文件的url正则表达式
82 | * @return 增强后输出的内容
83 | */
84 | public static String enhanceContent(String content, String allowUrlRegular) {
85 | if ((allowUrlRegular == null) || (allowUrlRegular.trim().equals(""))){
86 | allowUrlRegular = "*";
87 | }
88 |
89 | String result = content;
90 |
91 | /**
92 | * get first line content
93 | */
94 | String[] lines = content.split(SPLITTER_HEAD_FIRST_LINE);
95 | if (lines == null || lines.length <= 1) {
96 | return result;
97 | }
98 | String firstLine = lines[0];
99 | String secondLine = lines[1];
100 |
101 | String[] firstLineItems = firstLine.split(SPLITTER_HEADER_ITEM);
102 | if (firstLineItems.length != 3) {
103 | return result;
104 | } else {
105 | String payloadType = firstLineItems[0];
106 | String requestId = firstLineItems[1];
107 |
108 | if (PAYLOAD_TYPE_REQUEST.equals(payloadType)) {
109 | String[] secondLineItems = secondLine.split(SPLITTER_HEADER_ITEM);
110 | String url = secondLineItems[1];
111 | String uri = url;
112 | int urlIndex = url.indexOf("?");
113 | if (urlIndex > 0) {
114 | uri = url.substring(0, urlIndex);
115 | }
116 |
117 | String requestIdPair = INJECT_TO_REQUEST_ENTITY_REQUEST_ID + "=" + requestId + "&";
118 | result = content.replaceFirst(SPLITTER_HEADER_BODY_SPLITTER, SPLITTER_HEADER_BODY_SPLITTER + requestIdPair);
119 |
120 | boolean isMatch = false;
121 | String[] allowUrls = allowUrlRegular.split(",");
122 | for (String allowUrl : allowUrls) {
123 | if (uri.matches(allowUrl)){
124 | recordRequestIds.add(requestId);
125 | isMatch = true;
126 | break;
127 | }
128 | }
129 | if(!isMatch){
130 | //URL不能匹配上的则不输出到文件
131 | result = "";
132 | }
133 |
134 | } else if (PAYLOAD_TYPE_ORIGINAL_RESPONSE.equals(payloadType)) {
135 | if (recordRequestIds.contains(requestId)) {
136 | recordRequestIds.remove(requestId);
137 | } else {//不再recordRequestIds记录中则不输出到文件
138 | result = "";
139 | }
140 | }
141 | }
142 |
143 | return result;
144 | }
145 |
146 | /**
147 | * java go.GorEnhance
148 | *
149 | * @param args
150 | * @throws Exception
151 | */
152 | public static void main(String[] args) throws Exception {
153 |
154 | String line;
155 | StringBuilder allowUrlRegular = new StringBuilder();
156 | int bytesRead = 0;
157 | byte[] buffer = new byte[1024];
158 |
159 | try (BufferedInputStream bufferedInput = new BufferedInputStream(Class.class.getClassLoader().getSystemResourceAsStream("go/middleware/allow-url.txt"))) {
160 | while ((bytesRead = bufferedInput.read(buffer)) != -1) {
161 | allowUrlRegular.append(new String(buffer, 0, bytesRead));
162 | }
163 | }
164 |
165 | BufferedReader stdin = new BufferedReader(new InputStreamReader(
166 | System.in));
167 | while ((line = stdin.readLine()) != null) {
168 | System.out.println(encodeHex(enhanceContent(hexDecode(line), allowUrlRegular.toString())));
169 | }
170 |
171 | }
172 | }
173 |
--------------------------------------------------------------------------------
/src/go/middleware/allow-url.txt:
--------------------------------------------------------------------------------
1 | .*/order/confirmOrder.*,.*/order/generate.*,.*/order/customziationGenerate.*
--------------------------------------------------------------------------------
/src/go/urlJsonIgnorePath.properties:
--------------------------------------------------------------------------------
1 | .*8dolLogin.*=.*#data,.*#msg,.*#timestamp
2 | .*generateToken.*=.*#data
3 |
4 |
5 |
--------------------------------------------------------------------------------