Pure Shell HTTP Server - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
爱意满满的作品展示区。
baobao1270
V2EX    分享创造

Pure Shell HTTP Server

  •  
  •   baobao1270
    baobao1270 268 天前 3210 次点击
    这是一个创建于 268 天前的主题,其中的信息可能已经有所发展或是发生改变。
    1. 这是一个纯 shell 实现的 HTTP 服务器,能够使用 GET/POST 请求读写 Working directory 下的文件。
    2. 可以通过 PORT 和 KEY 环境变量设置监听的端口和写入操作的 API Key 。如果没有指定,那么会自动生成一个 UUID 作为 API Key ,并且在控制台打印出来。写入操作应该设置 Authorization 头为: APIKey 你的 API 密钥
    3. 对于中文或者没有正确 URL Encode 的请求支持应该不太好。
    4. 如果想要结束服务器,请在服务器目录下运行 kill -9 $(cat server.pid)
    5. 虽然做了一些安全措施,但是 shell 写的东西总归有些危险,不建议在有个人数据且暴露公网的设备上长期运行。
    6. 当然,这个服务器很简陋,也不支持多进程、多连接、可能有 race condition 问题,但是 just for fun ,更多的是作为一个「 shell 也可以写 HTTP 服务器」的概念验证。如果对代码有什么建议也欢迎提出。
    #!/bin/bash function server { read il echo "recv_il: $il" >&2 method=$( echo "$il" | cut -d" " -f1) path=$( echo "$il" | cut -d" " -f2) proto=$( echo "$il" | cut -d" " -f3) echo "method: $method" >&2 echo "path: $path" >&2 echo "proto: $proto" >&2 declare -A hdr while read line; do sline=`echo $line | tr -d '[\r\n]'` [ -z "$sline" ] && break echo "recv_hdr: $line" >&2 hdr_k=$(echo $line | cut -d":" -f1) hdr_v=$(echo $line | cut -d":" -f2- | cut -c2- | tr -d '[\r\n]') hdr[$hdr_k]="$hdr_v" done echo "recv_hdr_end" >&2 relpath=$(realpath "$(pwd)$path") if [[ "$relpath" != "$(pwd)"* ]]; then echo "possible path traversal attack: $relpath" >&2 echo "pwd: $(pwd)" >&2 echo "relpath: $relpath" >&2 echo -ne "HTTP/1.1 403 Forbidden\r\n" echo -ne "\r\n" echo -ne "possible path traversal attack: $relpath" exit 0 fi echo "relpath: $relpath" >&2 if [ $method = "GET" ]; then if [ ! -f "$relpath" ]; then echo -ne "HTTP/1.1 404 Not Found\r\n" echo -ne "\r\n" echo -ne "not found: $relpath" exit 0 fi echo -ne "HTTP/1.1 200\r\n" echo -ne "\r\n" cat "$relpath" exit 0 fi if [ $method != "POST" ]; then echo -ne "HTTP/1.1 405 Method Not Allowed\r\n" echo -ne "\r\n" exit 0 fi if [ "${hdr[Authorization]}" != "APIKey $KEY" ]; then echo -ne "HTTP/1.1 401 Unauthorized\r\n" echo -ne "\r\n" exit 0 fi body_file=$(mktemp) body_len=0 if [ ! -z "${hdr[Content-Length]}" ]; then echo "hdr_cl > body_len" >&2 body_len="${hdr[Content-Length]}" fi echo "body_len: $body_len" >&2 echo "body_file: $body_file" >&2 dd of=$body_file bs=1 count=$body_len mkdir -p $(dirname "$relpath") >&2 cp -v $body_file "$relpath" >&2 echo -ne "HTTP/1.1 201 Created\r\n" echo -ne "\r\n" echo -ne "created: $relpath\r\n" rm -rvf $body_file >&2 } if [ -z "$PORT" ]; then PORT=3000 fi if [ -z "$KEY" ]; then KEY=$(uuidgen) fi if [ "$EXEC" = "server" ]; then server exit 0 fi SCRIPT_DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &> /dev/null && pwd) PID=$$ PIDFILE="$SCRIPT_DIR/server.pid" if [[ -f "$PIDFILE" && -d "/proc/$(cat $PIDFILE)" ]]; then echo "one instance is running, refuse to start another" exit 1 fi if [ -f "server.sh" ]; then echo "DO NOT START SERVER WHEN CURRENT WORKING DIRECTORY IS SAME AS SCRIPT DIRECTORY" echo "THIS MAY CAUSE UNEXPECTED OVERWRITING SERVER AND RCE" echo "EXITING" exit 1 fi echo "PID=$PID" echo $PID > $PIDFILE echo "PIDFILE=$PIDFILE" echo "KEY=$KEY" while true; do nc -vlp $PORT -c "EXEC=server KEY=$KEY $0" done 
    11 条回复    2025-01-16 15:30:54 +08:00
    trepwq
        1
    trepwq  
       268 天前 via iPhone   1
    nc 换成 socat ,可以端口复用
    heimoshuiyu
        2
    heimoshuiyu  
       268 天前
    太酷辣
    sagaxu
        3
    sagaxu  
       268 天前
    python -m http.server $PORT
    php -S localhost:$PORT
    jwebserver -p $PORT
    ruby -run -e httpd -p $PORT
    baobao1270
        4
    baobao1270  
    OP
       268 天前
    @sagaxu 你这个要装编程环境啊,而且只能读不能写
    sagaxu
        5
    sagaxu  
       268 天前
    @baobao1270 大部分 Linux 发行版依赖 Python ,不用另外安装。以前写 web 服务的时候,直接用 linux 自带的 inetd 监听端口,收到请求时调用 CGI 调用处理程序,CGI 可以是任何语言写的,只要这个语言能读 stdio 和环境变量以及写入 stdout 。像 Ubuntu 标准版自带的 busybox 也自带了一个 httpd ,可以提供 CGI 转发。
    zsh2517
        6
    zsh2517  
       268 天前   1
    @baobao1270 偏个题,如果考虑编程环境,并且需要上传的话,可以选择 https://pypi.org/project/uploadserver/ ,用法和 http.server 一样 python3 -m uploadserver ,不过不是标准库,需要 python 环境并且 pip 装包。

    ---

    我之前也想过,有文件、网路 IO 的情况下,shell 是不是也能作为 web 服务器,结果今天真看到有人实现出来了
    zsh2517
        7
    zsh2517  
       268 天前
    @zsh2517 网路 -> 网络,键盘输入丢了个 o
    w568w
        8
    w568w  
       267 天前   1
    cool ,这才是真正的 shell

    另有一些语法风格上的建议:

    1. function 关键字是兼容一些远古 shell 给出的。既然指定了 bash ,用 server() {} 就好了;

    2. 函数内的变量最好用 local 声明,否则作用域会泄漏到函数外;

    3. 可以用 shellcheck 过一遍,可能有其他忽略的点
    dianso
        9
    dianso  
       267 天前   1
    shell 做不到的。
    你这个依赖 netcat 啊

    和 python go 开个 https 有啥区别。
    ruzztok
        10
    ruzztok  
       267 天前
    nc ,可不是所有系统都自带哦。。
    ai277014717
        11
    ai277014717  
       267 天前
    可以再交给 GPT 润色一下
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     1400 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 24ms UTC 16:42 PVG 00:42 LAX 09:42 JFK 12:42
    Do have faith in what you're doing.
    ubao snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86