使用netcat nc创建一个最小的REST Web服务器

9
我正在寻找最小的REST Web服务器,使用nc命令成为Docker容器的单行代码。例如:
* http://localhost/echo/marcello: prints marcello
* http://localhost/date: prints the server's date
* http://localhost/...: others

我在看这个问题 "使用netcat创建最小化web服务器",但它将调用代理到shell脚本...我只需要像以下一行命令那样的简单命令
while true ; do nc -l -p 1500 -c 'echo -e "HTTP/1.1 200 OK\n\n $(date)"'; done

另外一种解决方案是冗长的,并且需要从文件中加载...
2个回答

23

我黑掉了 @syme 在 "https://dev59.com/smQn5IYBdhLWcg3wr406#24342101" 给出的示例,并创建了一行代码的REST服务器。虽然有些标头缺失,但它可以正确处理HTTP GET和未实现资源的404。

rm -f out ; mkfifo out ; trap "rm -f out" EXIT ; while true ; do cat out | nc -l 1500 > >(export REQUEST= ; while read line ; do line=$(echo "$line" | tr -d '[\r\n]') ; if echo "$line" | grep -qE '^GET /' ; then REQUEST=$(echo "$line" | cut -d ' ' -f2) ; elif [ "x$line" = x ] ; then HTTP_200="HTTP/1.1 200 OK" ; HTTP_LOCATION="Location:" ; HTTP_404="HTTP/1.1 404 Not Found" ; if echo $REQUEST | grep -qE '^/echo/' ; then printf "%s\n%s %s\n\n%s\n" "$HTTP_200" "$HTTP_LOCATION" $REQUEST ${REQUEST#"/echo/"} > out ; elif echo $REQUEST | grep -qE '^/date' ; then date > out ; elif echo $REQUEST | grep -qE '^/stats' ; then vmstat -S M > out ; elif echo $REQUEST | grep -qE '^/net' ; then ifconfig > out ; else printf "%s\n%s %s\n\n%s\n" "$HTTP_404" "$HTTP_LOCATION" $REQUEST "Resource $REQUEST NOT FOUND!" > out ; fi ; fi ; done) ; done

格式化版本位于https://gist.github.com/marcellodesales/9e4288f35ac2cc3e1b83#file-formatted

上述API实现了以下功能:

  • /echo/{name}

返回给定的{name}

$ curl -i http://localhost:1500/echo/marcello
HTTP/1.1 200 OK
Location: /echo/marcello

marcello
  • /date

返回服务器的日期

$ curl -i http://localhost:1500/date
Sun Oct 19 14:12:27 PDT 2014
  • /stats

返回服务器的统计信息

$ curl -i http://localhost:1500/stats
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
 r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
 0  0     11    374    383   2198    0    0     6    22   33    8  2  2 97  0  0
  • /net

显示服务器的网络

$ curl -i http://localhost:1500/net
docker0   Link encap:Ethernet  HWaddr 56:84:7a:fe:97:99  
          inet addr:172.17.42.1  Bcast:0.0.0.0  Mask:255.255.0.0
          inet6 addr: fe80::5484:7aff:fefe:9799/64 Scope:Link
          UP BROADCAST MULTICAST  MTU:1500  Metric:1
          RX packets:120694 errors:0 dropped:0 overruns:0 frame:0
          TX packets:141757 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0 
          RX bytes:272911625 (272.9 MB)  TX bytes:289945068 (289.9 MB)

eth0      Link encap:Ethernet  HWaddr 00:0c:29:1f:d3:b5  
          inet addr:192.168.248.206  Bcast:192.168.248.255  Mask:255.255.255.0
          inet6 addr: fe80::20c:29ff:fe1f:d3b5/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:2322493 errors:0 dropped:0 overruns:0 frame:0
          TX packets:1098965 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000 
          RX bytes:2367412677 (2.3 GB)  TX bytes:700548644 (700.5 MB)

lo        Link encap:Local Loopback  
          inet addr:127.0.0.1  Mask:255.0.0.0
          inet6 addr: ::1/128 Scope:Host
          UP LOOPBACK RUNNING  MTU:65536  Metric:1
          RX packets:151566 errors:0 dropped:0 overruns:0 frame:0
          TX packets:151566 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0 
          RX bytes:305833574 (305.8 MB)  TX bytes:305833574 (305.8 MB)
  • /任何东西/未实现

对于服务器未实现的任何内容,都会打印404消息。

$ curl -i http://localhost:1500/wrong
HTTP/1.1 404 Not Found
Location: /wrong

Resource /wrong NOT FOUND!

这是来自上面GIST的格式化解决方案。您可以将其保存为"web.sh"并运行:)

rm -f out
mkfifo out
trap "rm -f out" EXIT
while true
do
  cat out | nc -l -p 1500 -q 1 > >( # parse the netcat output, to build the answer redirected to the pipe "out".
    export REQUEST=
    while read line
    do
      line=$(echo "$line" | tr -d '[\r\n]')
 
      if echo "$line" | grep -qE '^GET /' # if line starts with "GET /"
      then
        REQUEST=$(echo "$line" | cut -d ' ' -f2) # extract the request
      elif [ "x$line" = x ] # empty line / end of request
      then
        HTTP_200="HTTP/1.1 200 OK"
        HTTP_LOCATION="Location:"
        HTTP_404="HTTP/1.1 404 Not Found"
        # call a script here
        # Note: REQUEST is exported, so the script can parse it (to answer 200/403/404 status code + content)
        if echo $REQUEST | grep -qE '^/echo/'
        then
            printf "%s\n%s %s\n\n%s\n" "$HTTP_200" "$HTTP_LOCATION" $REQUEST ${REQUEST#"/echo/"} > out
        elif echo $REQUEST | grep -qE '^/date'
        then
            date > out
        elif echo $REQUEST | grep -qE '^/stats'
        then
            vmstat -S M > out
        elif echo $REQUEST | grep -qE '^/net'
        then
            ifconfig > out
        else
            printf "%s\n%s %s\n\n%s\n" "$HTTP_404" "$HTTP_LOCATION" $REQUEST "Resource $REQUEST NOT FOUND!" > out
        fi
      fi
    done
  )
done

1
一个写得很好的答案,对于一个好的hack来说非常不错,但是,拜托,一行代码宽了半英里?你的解释结构很好,为什么代码不一样呢 ;-)!? - shellter
2
我对Docker不是很了解,但我已经做过类似的事情(不是那么多),作为一个crontab条目。感谢更新,更容易看到你在做什么;-)。祝好运。 - shellter
2
我不得不在nc命令中添加-p和-q选项才能使其正常工作:“nc -l -p 1500 -q 1”。 - Gyro Gearloose
就像@GyroGearloose一样,我不得不添加“nc -l -p 1500 -q1”才能使其按预期工作。 - torsan
@torsan 謝謝你!我已經更新了答案。 - Marcello DeSales
显示剩余2条评论

0
另一个使用NetCat的Bash完整且简单的解决方案如下。它支持Content-type、文件大小等等。最新版本可在https://github.com/jdoleczek/Bash-NetCat-HTTPD/blob/master/httpd.sh中找到。
#!/bin/bash
# LICENSE MIT

PORT=${1:-8080}
FILES=${2:-"./"}

NS=$(netstat -taupen 2>/dev/null | grep ":$PORT ")
test -n "$NS" && echo "Port $PORT is already taken" && exit 1

echo -e "\n\tHTTPD started for files in $FILES:"

for IP in $(ifconfig | grep "inet addr" | cut -d ':' -f 2 | cut -d ' ' -f 1) ; do
    echo -e "\tlistening at $IP:$PORT"
done

echo -e "\n"
FIFO="/tmp/httpd$PORT"
rm -f $FIFO
mkfifo $FIFO
trap ctrl_c INT

function ctrl_c() {
    rm -f $FIFO && echo -e "\n\tServer shut down.\n" && exit
}

while true; do (
    read req < $FIFO;
    req=$(echo $req | cut -d" " -f2 | cut -d"#" -f1 | cut -d"?" -f1 | cut -c2-);
    >&2 echo -e -n "\tRequest: \"$req\"\t";
    test -z "$req" && req="index.html"

    if [ -f "$FILES$req" ] ; then
        ext="${req##*.}"
        ext=$(echo "$ext" | tr '[:upper:]' '[:lower:]')

        case "$ext" in
            "html" | "htm") CONTENTTYPE="text/html; charset=UTF-8" ;;
            "json") CONTENTTYPE="application/json; charset=UTF-8" ;;
            "css" | "less" | "sass") CONTENTTYPE="text/css" ;;
            "txt") CONTENTTYPE="text/plain" ;;
            "xml") CONTENTTYPE="text/xml" ;;
            "js") CONTENTTYPE="application/javascript" ;;
            "jpg" | "jpeg") CONTENTTYPE="image/jpeg" ;;
            "png") CONTENTTYPE="image/png" ;;
            "gif") CONTENTTYPE="image/gif" ;;
            "ico") CONTENTTYPE="image/x-icon" ;;
            "wav") CONTENTTYPE="audio/wav" ;;
            "mp3") CONTENTTYPE="audio/mpeg3" ;;
            "avi") CONTENTTYPE="video/avi" ;;
            "mp4" | "mpg" | "mpeg" | "mpe") CONTENTTYPE="video/mpeg" ;;
            *) CONTENTTYPE="application/octet-stream"
        esac

        echo "HTTP/1.x 200 OK"
        echo "Date: $(LC_TIME=en_US date -u)"
        echo "Server: promyk.doleczek.pl"
        echo "Connection: close"
        echo "Pragma: public"
        echo "Content-Type: $CONTENTTYPE"
        FILESIZE=$(wc -c < "$FILES$req")
        echo -e "Content-Length: $FILESIZE\n"
        cat "$FILES$req"
        >&2 echo "[ ok ]"
    else
        echo -e "HTTP/1.x 404 Not found\n\n<h1>File not found.</h1>"
        >&2 echo "[ no file ]"
    fi
) | nc -l -k -w 1 -p $PORT > $FIFO; done;

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接