fix: avoid StackOverflowError when parsing upload payload
This commit is contained in:
@@ -1,6 +0,0 @@
|
||||
---
|
||||
alwaysApply: true
|
||||
---
|
||||
|
||||
Please execute the generate commit message command in Chinese.
|
||||
生成 Git 提交信息(Commit Message)时,必须强制使用中文。
|
||||
@@ -51,7 +51,6 @@ public class RedisClipSync {
|
||||
// 公共上传通道
|
||||
private static final String UPLOAD_CHANNEL = "global_clip_upload";
|
||||
private static final String UNKNOWN_SLAVE_ID = "unknown";
|
||||
private static final Pattern UPLOAD_PAYLOAD_PATTERN = Pattern.compile("\\{\"slaveId\":\"((?:\\\\.|[^\"])*)\",\"content\":\"((?:\\\\.|[^\"])*)\",\"timestamp\":(\\d+)\\}");
|
||||
private static final String MASTER_TO_SLAVE_LOG_FILE = "master_to_slave.log";
|
||||
private static final String SLAVE_TO_MASTER_LOG_FILE = "slave_to_master.log";
|
||||
private static final String LOG_ENTRY_SEPARATOR = "\n\n------------------\n\n";
|
||||
@@ -551,14 +550,112 @@ public class RedisClipSync {
|
||||
return null;
|
||||
}
|
||||
|
||||
Matcher matcher = UPLOAD_PAYLOAD_PATTERN.matcher(message);
|
||||
if (!matcher.matches()) {
|
||||
// Avoid regex here: large clipboard payloads can trigger deep backtracking and StackOverflowError.
|
||||
// We only need to parse a fixed JSON shape produced by buildUploadPayload().
|
||||
try {
|
||||
String trimmed = message.trim();
|
||||
if (!trimmed.startsWith("{") || !trimmed.endsWith("}")) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String slaveIdRaw = extractJsonStringField(trimmed, "slaveId");
|
||||
String contentRaw = extractJsonStringField(trimmed, "content");
|
||||
// timestamp is optional for parsing; we just validate it exists and is numeric when present
|
||||
if (!hasJsonNumberField(trimmed, "timestamp")) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String slaveId = unescapeJson(slaveIdRaw);
|
||||
String content = unescapeJson(contentRaw);
|
||||
return new UploadMessage(slaveId, content);
|
||||
} catch (Exception ignored) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
String slaveId = unescapeJson(matcher.group(1));
|
||||
String content = unescapeJson(matcher.group(2));
|
||||
return new UploadMessage(slaveId, content);
|
||||
static String extractJsonStringField(String json, String fieldName) {
|
||||
int namePos = indexOfJsonFieldName(json, fieldName);
|
||||
if (namePos < 0) {
|
||||
throw new IllegalArgumentException("missing field: " + fieldName);
|
||||
}
|
||||
|
||||
int colon = json.indexOf(':', namePos);
|
||||
if (colon < 0) {
|
||||
throw new IllegalArgumentException("bad json: missing colon for " + fieldName);
|
||||
}
|
||||
|
||||
int i = colon + 1;
|
||||
while (i < json.length() && Character.isWhitespace(json.charAt(i))) {
|
||||
i++;
|
||||
}
|
||||
if (i >= json.length() || json.charAt(i) != '"') {
|
||||
throw new IllegalArgumentException("bad json: expected string for " + fieldName);
|
||||
}
|
||||
|
||||
return parseJsonStringBody(json, i);
|
||||
}
|
||||
|
||||
static boolean hasJsonNumberField(String json, String fieldName) {
|
||||
int namePos = indexOfJsonFieldName(json, fieldName);
|
||||
if (namePos < 0) {
|
||||
return false;
|
||||
}
|
||||
int colon = json.indexOf(':', namePos);
|
||||
if (colon < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int i = colon + 1;
|
||||
while (i < json.length() && Character.isWhitespace(json.charAt(i))) {
|
||||
i++;
|
||||
}
|
||||
int start = i;
|
||||
while (i < json.length()) {
|
||||
char c = json.charAt(i);
|
||||
if (c < '0' || c > '9') {
|
||||
break;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
return i > start;
|
||||
}
|
||||
|
||||
static int indexOfJsonFieldName(String json, String fieldName) {
|
||||
// Find: "fieldName" (as a JSON string token)
|
||||
String needle = "\"" + fieldName + "\"";
|
||||
return json.indexOf(needle);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse JSON string starting at the opening quote and return the raw (escaped) body.
|
||||
* Example input: "a\\n\"b" -> returns: a\\n\"b (without surrounding quotes)
|
||||
*/
|
||||
static String parseJsonStringBody(String json, int openingQuoteIndex) {
|
||||
if (openingQuoteIndex < 0 || openingQuoteIndex >= json.length() || json.charAt(openingQuoteIndex) != '"') {
|
||||
throw new IllegalArgumentException("bad json: expected opening quote");
|
||||
}
|
||||
|
||||
StringBuilder rawEscaped = new StringBuilder();
|
||||
int i = openingQuoteIndex + 1;
|
||||
while (i < json.length()) {
|
||||
char c = json.charAt(i);
|
||||
if (c == '"') {
|
||||
return rawEscaped.toString();
|
||||
}
|
||||
if (c == '\\') {
|
||||
if (i + 1 >= json.length()) {
|
||||
throw new IllegalArgumentException("bad json: trailing backslash");
|
||||
}
|
||||
rawEscaped.append('\\').append(json.charAt(i + 1));
|
||||
i += 2;
|
||||
continue;
|
||||
}
|
||||
|
||||
rawEscaped.append(c);
|
||||
i++;
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("bad json: unterminated string");
|
||||
}
|
||||
|
||||
static String escapeJson(String value) {
|
||||
|
||||
Reference in New Issue
Block a user