You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 05:39:49