Add files via upload

This commit is contained in:
公明
2025-11-24 00:37:59 +08:00
committed by GitHub
parent c0b01ea1cf
commit 138119a6a8

View File

@@ -168,6 +168,124 @@ args:
except (TypeError, ValueError):
return None
def encode_form_data(data: str):
"""
对application/x-www-form-urlencoded格式的数据进行URL编码
解析key=value格式对值部分进行URL编码保持键名不变
支持多个键值对(用&分隔)
正确处理值中包含=的情况使用split('=', 1)只分割第一个=
智能处理值中包含&的情况(通过检查&后是否跟着key=模式来判断)
注意:假设输入是未编码的原始数据,如果已经是编码的,可能会重复编码
"""
if not data:
return data
# 智能分割:找到真正的键值对分隔符&
# &是分隔符的条件:&后面跟着key=模式(即非空白字符后跟=
def find_key_value_pairs(text):
"""找到所有的key=value对正确处理值中的&和="""
pairs = []
i = 0
text_len = len(text)
while i < text_len:
# 跳过空白
while i < text_len and text[i] in ' \t\n\r':
i += 1
if i >= text_len:
break
# 查找键名(到第一个=为止)
key_start = i
while i < text_len and text[i] != '=':
i += 1
if i >= text_len:
# 没有=,可能是单个值
remaining = text[key_start:].strip()
if remaining:
pairs.append((None, remaining))
break
# 提取键名
key = text[key_start:i].strip()
i += 1 # 跳过=
if not key:
continue
# 查找值的结束位置
# 值可能包含&,需要判断&是否是分隔符
value_start = i
value_end = text_len
# 从值开始位置查找&
j = value_start
while j < text_len:
if text[j] == '&':
# 检查&后面是否跟着key=模式
k = j + 1
# 跳过空白
while k < text_len and text[k] in ' \t\n\r':
k += 1
# 查找下一个=或&
m = k
while m < text_len and text[m] not in '=&':
m += 1
# 如果找到=,说明&后面是新的键值对
if m < text_len and text[m] == '=':
value_end = j
i = j + 1 # 从&后开始下一轮
break
j += 1
# 提取值
value = text[value_start:value_end]
pairs.append((key, value))
if value_end < text_len:
i = value_end + 1
else:
break
return pairs
# 解析所有键值对
pairs = find_key_value_pairs(data)
parts = []
for key, value in pairs:
if key is None:
# 没有键,只有值
parts.append(urllib.parse.quote_plus(value, safe=''))
else:
# 对值进行URL编码
encoded_value = urllib.parse.quote_plus(value, safe='')
parts.append(f"{key}={encoded_value}")
return '&'.join(parts)
def should_encode_data(headers: list, data: str):
"""
判断是否需要对POST数据进行URL编码
如果Content-Type是application/x-www-form-urlencoded则需要编码
"""
if not data:
return False
content_type = None
for header in headers:
if ':' in header:
h_key, h_value = header.split(':', 1)
if h_key.strip().lower() == 'content-type':
content_type = h_value.strip().lower()
break
if content_type and 'application/x-www-form-urlencoded' in content_type:
return True
return False
parser = argparse.ArgumentParser(description="Enhanced HTTP testing helper")
parser.add_argument("--url", required=True)
parser.add_argument("--method", default="GET")
@@ -189,6 +307,7 @@ args:
parser.add_argument("--show-command", dest="show_command", action="store_true")
parser.add_argument("--show-summary", dest="show_summary", action="store_true")
parser.add_argument("--response-encoding", dest="response_encoding", default="")
parser.add_argument("--debug", dest="debug", action="store_true")
args = parser.parse_args()
repeat = max(1, args.repeat)
@@ -220,10 +339,52 @@ args:
curl_cmd.append("-k")
if args.proxy:
curl_cmd.extend(["-x", args.proxy])
for header in parse_headers(args.headers):
# 解析headers以便检查Content-Type
parsed_headers = parse_headers(args.headers)
for header in parsed_headers:
curl_cmd.extend(["-H", header])
if args.data:
curl_cmd.extend(["--data", args.data])
# 处理POST数据如果是表单数据需要URL编码
prepared_data = args.data
if args.debug and args.data:
print("\n===== Debug: POST Data Processing =====")
print(f"Original data: {repr(args.data)}")
print(f"Data length: {len(args.data)} bytes")
# 显示原始数据中的键值对
if '=' in args.data:
parts = args.data.split('&')
print(f"Detected {len(parts)} key-value pair(s):")
for i, part in enumerate(parts, 1):
if '=' in part:
k, v = part.split('=', 1)
print(f" [{i}] {k} = {repr(v[:50])}{'...' if len(v) > 50 else ''} (length: {len(v)})")
else:
print(f" [{i}] (no key): {repr(part[:50])}{'...' if len(part) > 50 else ''}")
if should_encode_data(parsed_headers, args.data):
print("Content-Type detected: application/x-www-form-urlencoded")
print("Encoding will be applied...")
else:
print("No encoding needed (not form-urlencoded)")
if args.data and should_encode_data(parsed_headers, args.data):
prepared_data = encode_form_data(args.data)
if args.debug:
print(f"\nEncoded data: {repr(prepared_data)}")
print(f"Encoded length: {len(prepared_data)} bytes")
# 显示编码后的键值对
if '&' in prepared_data:
encoded_parts = prepared_data.split('&')
print(f"Encoded into {len(encoded_parts)} key-value pair(s):")
for i, part in enumerate(encoded_parts, 1):
if '=' in part:
k, v = part.split('=', 1)
print(f" [{i}] {k} = {repr(v[:80])}{'...' if len(v) > 80 else ''} (length: {len(v)})")
else:
print(f" [{i}] (no key): {repr(part[:80])}{'...' if len(part) > 80 else ''}")
if prepared_data:
curl_cmd.extend(["--data", prepared_data])
metrics_template = METRIC_MARKER + ":" + "|".join([
"%{time_namelookup}",
"%{time_connect}",
@@ -425,6 +586,12 @@ parameters:
required: false
default: true
flag: "--show-summary"
- name: "debug"
type: "bool"
description: "调试模式打印POST数据的原始值和编码后的值方便排查编码问题"
required: false
default: false
flag: "--debug"
- name: "response_encoding"
type: "string"
description: "强制响应解码使用的编码默认自动检测可用于指定GBK等场景"