Shell脚本中使用curl发起HTTP请求返回404问题求助
带重试逻辑的Shell curl函数返回404问题排查
我编写了一个带重试逻辑的Shell脚本函数sendWithRetry,该函数接收URL和若干参数,用于构造并执行curl请求,但调用后返回404错误,怀疑是函数参数传递方式导致的。
函数代码
function sendWithRetry() { local _URL="$1" local _CURL_ARGS="${@:2}" RESPONSE="" for x in {0..20} ; do echo "Sending request to $_URL" cat ${CALLBACK_REQUEST_IPV4_HTTPS} echo "Executing : curl --connect-timeout 1 -f -s ${_CURL_ARGS} $_URL " RESPONSE=$(curl --connect-timeout 1 -f -s ${_CURL_ARGS} $_URL) && break done if [ $x -lt 20 ] ; then echo "Request $_URL sent successfully!!!" echo "Response: $RESPONSE" else echo "Failed to send request to $_URL!!!" exit 1 fi }
调用代码片段
if ! sendWithRetry "${TEST_URL_HTTPS}" -k -X POST -H '"Content-Type: application/json"' -d @${CALLBACK_REQUEST_IPV4_HTTPS} then echo "Test failed!!!" exit 1 fi
执行日志
[INFO] [exec] # $$ echo "Sending request to $_URL" [INFO] [exec] # Sending request to https://192.168.16.5:31444/service-b/v1/subscribe?p2=test [INFO] [exec] # $$ cat ${CALLBACK_REQUEST_IPV4_HTTPS} [INFO] [exec] # { [INFO] [exec] # "testname": "service-b makes a callback request to service-a over IPv4 & HTTPS protocol", [INFO] [exec] # "Uri": "https://192.168.16.5:30443/callback/service-b/v1/handleNotify", [INFO] [exec] # "p3": "param-3", [INFO] [exec] # "p5": "param-5" [INFO] [exec] # }$$ echo "Executing : curl --connect-timeout 1 -f ${_CURL_ARGS} $_URL " [INFO] [exec] # Executing : curl --connect-timeout 1 -f -v -k -X POST -H "Content-Type: application/json" -d @/test/scripts/tests/dual-stack/callback-flow/callbackRequestIpv4Https.json https://192.168.16.5:31444/service-b/v1/subscribe?p2=test [INFO] [exec] # $$ RESPONSE=$(curl --connect-timeout 1 -f -s ${_CURL_ARGS} $_URL) [INFO] [exec] # $$ curl --connect-timeout 1 -f -s ${_CURL_ARGS} $_URL [INFO] [exec] # * Could not resolve host: application [INFO] [exec] # * Closing connection [INFO] [exec] # * Trying 192.168.16.5:31444... [INFO] [exec] # * Connected to 192.168.16.5 (192.168.16.5) port 31444 [INFO] [exec] # * ALPN: curl offers h2,http/1.1 [INFO] [exec] # } [5 bytes data] [INFO] [exec] # * TLSv1.3 (OUT), TLS handshake, Client hello (1): [INFO] [exec] # } [512 bytes data] [INFO] [exec] # * TLSv1.3 (IN), TLS handshake, Server hello (2): [INFO] [exec] # { [122 bytes data] [INFO] [exec] # * TLSv1.3 (IN), TLS handshake, Encrypted Extensions (8): [INFO] [exec] # { [15 bytes data] [INFO] [exec] # * TLSv1.3 (IN), TLS handshake, Request CERT (13): [INFO] [exec] # { [83 bytes data] [INFO] [exec] # * TLSv1.3 (IN), TLS handshake, Certificate (11): [INFO] [exec] # { [670 bytes data] [INFO] [exec] # * TLSv1.3 (IN), TLS handshake, CERT verify (15): [INFO] [exec] # { [264 bytes data] [INFO] [exec] # * TLSv1.3 (IN), TLS handshake, Finished (20): [INFO] [exec] # { [52 bytes data] [INFO] [exec] # * TLSv1.3 (OUT), TLS change cipher, Change cipher spec (1): [INFO] [exec] # } [1 bytes data] [INFO] [exec] # * TLSv1.3 (OUT), TLS handshake, Certificate (11): [INFO] [exec] # } [8 bytes data] [INFO] [exec] # * TLSv1.3 (OUT), TLS handshake, Finished (20): [INFO] [exec] # } [52 bytes data] [INFO] [exec] # * SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384 / x25519 / RSASSA-PSS [INFO] [exec] # * ALPN: server accepted h2 [INFO] [exec] # * Server certificate: [INFO] [exec] # * subject: O=Openet; CN=Ingress for svc-b-ingress [INFO] [exec] # * start date: Oct 14 08:30:17 2024 GMT [INFO] [exec] # * expire date: Apr 12 08:30:17 2025 GMT [INFO] [exec] # * issuer: O=Openet; CN=Openet Test CA [INFO] [exec] # * SSL certificate verify result: unable to get local issuer certificate (20), continuing anyway. [INFO] [exec] # * Certificate level 0: Public key type RSA (2048/112 Bits/secBits), signed using ecdsa-with-SHA256 [INFO] [exec] # } [5 bytes data] [INFO] [exec] # * using HTTP/2 [INFO] [exec] # * [HTTP/2] [1] OPENED stream for https://192.168.16.5:31444/service-b/v1/subscribe?p2=test [INFO] [exec] # * [HTTP/2] [1] [:method: POST] [INFO] [exec] # * [HTTP/2] [1] [:scheme: https] [INFO] [exec] # * [HTTP/2] [1] [:authority: 192.168.16.5:31444] [INFO] [exec] # * [HTTP/2] [1] [:path: /service-b/v1/subscribe?p2=test] [INFO] [exec] # * [HTTP/2] [1] [user-agent: curl/8.8.0] [INFO] [exec] # * [HTTP/2] [1] [accept: */*] [INFO] [exec] # * [HTTP/2] [1] [content-length: 209] [INFO] [exec] # * [HTTP/2] [1] [content-type: application/x-www-form-urlencoded] [INFO] [exec] # } [5 bytes data] [INFO] [exec] # > POST /service-b/v1/subscribe?p2=test HTTP/2 [INFO] [exec] # > Host: 192.168.16.5:31444 [INFO] [exec] # > User-Agent: curl/8.8.0 [INFO] [exec] # > Accept: */* [INFO] [exec] # > Content-Length: 209 [INFO] [exec] # > Content-Type: application/x-www-form-urlencoded [INFO] [exec] # > [INFO] [exec] # } [209 bytes data] [INFO] [exec] # * upload completely sent off: 209 bytes [INFO] [exec] # { [5 bytes data] [INFO] [exec] # * TLSv1.3 (IN), TLS handshake, Newsession Ticket (4): [INFO] [exec] # { [230 bytes data] [INFO] [exec] # * TLSv1.3 (IN), TLS handshake, Newsession Ticket (4): [INFO] [exec] # { [230 bytes data] [INFO] [exec] # * old SSL session ID is stale, removing [INFO] [exec] # { [5 bytes data] [INFO] [exec] # < HTTP/2 404 [INFO] [exec] # < content-type: application/problem+json [INFO] [exec] # < 3gpp-sbi-message-priority: 24 [INFO] [exec] # < content-length: 37 [INFO] [exec] # < x-envoy-upstream-service-time: 1 [INFO] [exec] # < date: Mon, 14 Oct 2024 15:05:10 GMT [INFO] [exec] # < server: PCF-91fd8c65-677a-4292-9333-376ba9649ffd [INFO] [exec] # < [INFO] [exec] # * The requested URL returned error: 404
问题分析
从日志里能看到两个核心问题:
- 出现
* Could not resolve host: application错误,说明Content-Type的参数被错误拆分,curl把application当成了主机名 - 实际发送的请求中,
Content-Type被自动设为application/x-www-form-urlencoded,而非我们指定的application/json,这会导致服务器无法正确解析请求体,进而返回404
根本原因
- 调用参数错误:调用函数时,
-H '"Content-Type: application/json"'多嵌套了一层引号,导致shell解析时把"Content-Type:和application"拆成了两个独立参数 - 函数参数传递错误:函数中用
local _CURL_ARGS="${@:2}"将剩余参数拼接成字符串,这种方式会破坏参数的原始结构,带空格的参数会被错误拆分
修复方案
1. 修正调用代码的Header参数
去掉Header参数中多余的嵌套引号:
if ! sendWithRetry "${TEST_URL_HTTPS}" -k -X POST -H "Content-Type: application/json" -d @${CALLBACK_REQUEST_IPV4_HTTPS} then echo "Test failed!!!" exit 1 fi
2. 修改函数的参数传递方式
使用数组保存curl参数,避免字符串拼接导致的参数拆分问题:
function sendWithRetry() { local _URL="$1" shift # 移除第一个URL参数,剩余参数均为curl参数 local _CURL_ARGS=("$@") # 用数组保存参数,保留原始结构 RESPONSE="" for x in {0..20}; do echo "Sending request to $_URL" cat "${CALLBACK_REQUEST_IPV4_HTTPS}" # 打印执行命令时,用引号包裹数组元素,方便调试 echo "Executing : curl --connect-timeout 1 -f -s ${_CURL_ARGS[@]@Q} $_URL " # 使用数组展开方式传递参数,保证每个参数的完整性 RESPONSE=$(curl --connect-timeout 1 -f -s "${_CURL_ARGS[@]}" "$_URL") && break done if [ "$x" -lt 20 ]; then echo "Request $_URL sent successfully!!!" echo "Response: $RESPONSE" else echo "Failed to send request to $_URL!!!" exit 1 fi }
修复说明
- 用
shift配合数组_CURL_ARGS=("$@")保存curl参数,能完整保留带空格或特殊字符的参数结构 - 调用curl时使用
"${_CURL_ARGS[@]}"展开数组,确保每个参数作为独立项被curl接收 - 调试打印时用
${_CURL_ARGS[@]@Q}可以显示带引号的参数原始形态,方便排查问题
内容的提问来源于stack exchange,提问作者Starbucks Admin
相关产品推荐
相关产品推荐

