如何用Bash脚本+Ncat提取浏览器POST请求体数据?
基于Ncat的Bash HTTP服务器:提取POST请求体及请求数据
问题说明
作为Bash脚本新手,希望仅通过Bash脚本提取浏览器发送至Ncat搭建的Bash服务器的POST请求体数据,同时获取HTTP方法、路由等信息。此前尝试的代码存在无限循环、HTTP响应异常导致浏览器卡死的问题,无法正确获取请求数据。
原尝试代码
#!/bin/bash PORT=8080 DIR="/var/www/html" setup_nginx(){ sudo apt-get update sudo apt-get install -y nginx } configure_nginx(){ cat > /etc/nginx/sites-enabled/default << EOF server { listen 80; server_name localhost; root /var/www/html; index index.html index.htm index.nginx-debian.html; # Timeouts proxy_connect_timeout 60; proxy_read_timeout 60; proxy_send_timeout 60; # Buffer settings proxy_buffering off; proxy_request_buffering off; location / { proxy_pass http://localhost:$PORT; } } EOF sudo nginx -t && systemctl reload nginx } setup_ncat(){ sudo apt-get update sudo apt-get install -y ncat } create_HTML_page(){ cat > $DIR/index.html << EOF <!DOCTYPE html> <html> <head> <title>Bash Web Application</title> </head> <body> <h1>Save Messages</h1> <form action="/messages" method="POST"> <input type="text" name="message" placeholder="Enter your message"> <input type="submit" value="Submit"> </form> <br/> <a href="http://localhost:$PORT/messages">Messages Page</a> <br/> <a href="http://localhost:$PORT/deleteAll">Delete All Messages</a> </body> </html> EOF cat > $DIR/success.html << EOF <!DOCTYPE html> <html> <head> <title>Success</title> </head> <body> <h1>Your operation has been done successfully</h1> <a href="http://localhost:$PORT">Home Page</a> <br/> <a href="http://localhost:$PORT/messages">Messages Page</a> <br/> <a href="http://localhost:$PORT/deleteAll">Delete All Messages</a> </body> </html> EOF } # File to store messages DATA_FILE="$DIR/messages.txt" # Ensure the data file exists if [ ! -f "$DATA_FILE" ]; then cd "$DIR" touch messages.txt fi create_server(){ echo "Starting web server on port $PORT..." echo "Visit http://localhost:$PORT to view the website." while true; do # Listen for connections and process requests ncat -l -p $PORT -c ' DIR=/var/www/html set -x # Read the first line of the HTTP request (e.g., POST /endpoint HTTP/1.1) read -r REQUEST_FIRST_LINE METHOD=$(echo "$REQUEST_FIRST_LINE" | cut -d" " -f1) ROUTE=$(echo "$REQUEST_FIRST_LINE" | cut -d" " -f2) REQUEST="" if [ "$METHOD" = "POST" ]; then while IFS= read -r line; do # Extract Content-Length if present if echo "$line" | grep -q "^Content-Length:"; then CONTENT_LENGTH=$(echo "$line" | cut -d" " -f2) fi line=${line%%$'\r'} REQUEST+="$line"$'\n' if [ "$line" = " " ]; then # If we have Content-Length, read the body if [ ! -z "$CONTENT_LENGTH" ]; then # Read the POST body body=$(dd bs=1 count=$CONTENT_LENGTH 2>/dev/null) # Add body to complete request REUQEST+="$body" fi break fi done fi # Determine which file to serve based on the requested route case "$ROUTE" in "/") if [ "$METHOD" = "GET" ]; then FILE="$DIR/index.html" STATUS="200 OK" fi ;; "/messages") if [ "$METHOD" = "GET" ]; then FILE="$DIR/messages.txt" STATUS="200 OK" today=$(date +"%Y-%m-%d") elif [ "$METHOD" = "POST" ]; then read -r request # data=$(echo "$request" | sed -e "s/+/ /g" -e "s/.*message=([^&]*).*/\1/") data= $(echo "$request") echo "$data" >> $DIR/messages.txt FILE="$DIR/success.html" STATUS="200 OK" today=$(date +"%Y-%m-%d") fi ;; "/deleteAll") if [ "$METHOD" = "GET" ]; then > $DIR/messages.txt FILE="$DIR/success.html" STATUS="200 OK" today=$(date +"%Y-%m-%d") fi ;; *) FILE="templates/404.html" STATUS="404 Not Found" ;; esac # Check if the requested file exists if [ -f "$FILE" ]; then RESPONSE_BODY=$(cat "$FILE") RESPONSE_BODY=$(eval "echo \"$RESPONSE_BODY\"") else RESPONSE_BODY="<html><body><h1>404 Not Found</h1></body></html>" fi # Send the HTTP response headers and body echo "HTTP/1.1 $STATUS\r" echo "Content-Type: text/html\r" echo "Connection: close\r" echo "\r" # This is the required blank line separating headers from the body echo "$RESPONSE_BODY" ' if [[ "$?" -ne 0 ]]; then echo "Error happened during connection";; exit 1; fi done } # handle_http_request(){ # while true; do # echo "Listening on port $PORT..." # # Use netcat to listen for incoming connections # { # # Read the request line # read request # log_request "$request" # # Extract HTTP method and resource path # method=$(echo "$request" | awk '{print $1}') # resource=$(echo "$request" | awk '{print $2}') # # Process GET request # if [[ $method == "GET" ]]; then # echo -e "HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\n\r\nGET request received: $resource" # # Process POST request # elif [[ $method == "POST" ]]; then # # Read Content-Length header # while read header; do # [[ $header == $'\r' ]] && break # if [[ $header == Content-Length:* ]]; then # length=${header#Content-Length: } # fi # done # # Read the payload # read -n $length payload # echo "$payload" >> "$DATAFILE" # echo -e "HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\n\r\nPOST data saved: $payload" # # Handle unknown methods # else # echo -e "HTTP/1.1 405 Method Not Allowed\r\nContent-Type: text/plain\r\n\r\nUnsupported HTTP method: $method" # fi # } | nc -l -p "$PORT" -q 1 # done # } setup(){ command -v nginx if [[ "$?" -ne 0 ]]; then echo "nginx is not installed, it will be installed now" setup_nginx fi command -v ncat if [[ "$?" -ne 0 ]]; then echo "ncat is not installed, it will be installed now" setup_ncat fi configure_nginx create_HTML_page create_server } setup
原代码问题分析
- POST请求处理逻辑混乱:读取完请求体后又额外执行
read -r request,导致阻塞等待输入,引发浏览器卡死 - 变量拼写错误:
REUQEST应为REQUEST - HTTP响应格式错误:响应头的换行应使用
\r\n,且空行需正确分隔头和体 - 不必要的Nginx代理:直接用Ncat监听端口即可,无需额外代理层增加复杂度
修正后的完整代码
#!/bin/bash PORT=8080 DIR="/var/www/html" DATA_FILE="$DIR/messages.txt" # 确保目录和数据文件存在 mkdir -p "$DIR" touch "$DATA_FILE" # 创建前端页面 create_html_pages() { cat > "$DIR/index.html" << EOF <!DOCTYPE html> <html> <head> <title>Bash Web App</title> </head> <body> <h1>Save Messages</h1> <form action="/messages" method="POST"> <input type="text" name="message" placeholder="Enter your message"> <input type="submit" value="Submit"> </form> <br> <a href="/messages">View Messages</a> <br> <a href="/deleteAll">Delete All Messages</a> </body> </html> EOF cat > "$DIR/success.html" << EOF <!DOCTYPE html> <html> <head> <title>Success</title> </head> <body> <h1>Operation Completed</h1> <a href="/">Home</a> <br> <a href="/messages">View Messages</a> </body> </html> EOF } # 启动Ncat服务器 start_server() { echo "Server running on http://localhost:$PORT" echo "Press Ctrl+C to stop" while true; do ncat -l -p "$PORT" -c ' DIR="/var/www/html" DATA_FILE="$DIR/messages.txt" # 读取请求首行,提取方法和路由 read -r REQUEST_LINE METHOD=$(echo "$REQUEST_LINE" | awk "{print \$1}") ROUTE=$(echo "$REQUEST_LINE" | awk "{print \$2}") POST_BODY="" CONTENT_LENGTH=0 # 读取请求头,获取Content-Length while IFS= read -r HEADER_LINE; do # 去除回车符 HEADER_LINE=${HEADER_LINE%%$"\r"} # 遇到空行,结束头读取 if [ -z "$HEADER_LINE" ]; then break fi # 提取Content-Length if [[ "$HEADER_LINE" =~ ^Content-Length:[[:space:]]*([0-9]+) ]]; then CONTENT_LENGTH=${BASH_REMATCH[1]} fi done # 读取POST请求体 if [ "$METHOD" = "POST" ] && [ "$CONTENT_LENGTH" -gt 0 ]; then POST_BODY=$(dd bs=1 count="$CONTENT_LENGTH" 2>/dev/null) # 解码URL编码的空格 POST_BODY=${POST_BODY//+/ } fi # 处理路由逻辑 STATUS="200 OK" RESPONSE_BODY="" case "$ROUTE" in "/") if [ "$METHOD" = "GET" ]; then RESPONSE_BODY=$(cat "$DIR/index.html") else STATUS="405 Method Not Allowed" RESPONSE_BODY="<html><body><h1>405 Method Not Allowed</h1></body></html>" fi ;; "/messages") if [ "$METHOD" = "GET" ]; then # 读取消息文件,若无内容则提示 if [ -s "$DATA_FILE" ]; then RESPONSE_BODY="<html><body><h1>Messages</h1><pre>$(cat "$DATA_FILE")</pre><a href="/">Home</a></body></html>" else RESPONSE_BODY="<html><body><h1>No Messages</h1><a href="/">Home</a></body></html>" fi elif [ "$METHOD" = "POST" ]; then # 提取message参数值 MESSAGE=$(echo "$POST_BODY" | grep -oP "message=\K[^&]*") # 解码URL编码字符 MESSAGE=$(printf "%b" "${MESSAGE//%/\\x}") # 添加时间戳并写入文件 echo "[$(date +"%Y-%m-%d %H:%M:%S")] $MESSAGE" >> "$DATA_FILE" RESPONSE_BODY=$(cat "$DIR/success.html") else STATUS="405 Method Not Allowed" RESPONSE_BODY="<html><body><h1>405 Method Not Allowed</h1></body></html>" fi ;; "/deleteAll") if [ "$METHOD" = "GET" ]; then > "$DATA_FILE" RESPONSE_BODY=$(cat "$DIR/success.html") else STATUS="405 Method Not Allowed" RESPONSE_BODY="<html><body><h1>405 Method Not Allowed</h1></body></html>" fi ;; *) STATUS="404 Not Found" RESPONSE_BODY="<html><body><h1>404 Not Found</h1></body></html>" ;; esac # 发送标准HTTP响应 echo -ne "HTTP/1.1 $STATUS\r\n" echo -ne "Content-Type: text/html; charset=utf-8\r\n" echo -ne "Connection: close\r\n" echo -ne "Content-Length: ${#RESPONSE_BODY}\r\n" echo -ne "\r\n" echo -n "$RESPONSE_BODY" ' # 处理Ncat异常退出 if [ $? -ne 0 ]; then echo "Server stopped unexpectedly" exit 1 fi done } # 主执行流程 install_deps() { if ! command -v ncat &> /dev/null; then echo "Installing ncat..." sudo apt-get update && sudo apt-get install -y ncat fi } install_deps create_html_pages start_server
关键功能说明
- 请求数据提取:
- 通过
read读取请求首行,用awk提取HTTP方法和路由 - 遍历请求头,用正则匹配获取
Content-Length值 - 使用
dd读取对应长度的POST请求体,避免阻塞
- 通过
- POST体处理:
- 解码URL编码的空格和特殊字符
- 提取表单参数
message的值,添加时间戳后写入文件
- HTTP响应规范:
- 使用
echo -ne确保发送正确的\r\n换行符 - 包含
Content-Length头,避免浏览器等待超时 - 明确设置
Connection: close,确保连接正常关闭
- 使用
内容的提问来源于stack exchange,提问作者karim_mo
相关产品推荐
相关产品推荐

