【问题标题】:Minimal web server using netcat使用 netcat 的最小 Web 服务器
【发布时间】:2013-05-19 22:17:28
【问题描述】:

我正在尝试使用 netcat (nc) 设置一个最小的 Web 服务器。例如,当浏览器调用 localhost:1500 时,它应该显示一个函数的结果(以下示例中的 date,但最终它将是一个产生一些数据的 python 或 c 程序) . 我的小 netcat 网络服务器需要在 bash 中是一个 while true 循环,可能就这么简单:

while true ; do  echo -e "HTTP/1.1 200 OK\n\n $(date)" | nc -l -p 1500  ; done

当我尝试这个时,浏览器会在 nc 启动时显示当前可用的数据。不过,我希望浏览器在浏览器请求数据的那一刻显示数据。我怎样才能做到这一点?

【问题讨论】:

  • 感谢大家的建议,这些建议使我进行了更多的谷歌搜索。碰巧我在link 遇到了一个完全不同的解决方案。我不能使用 python 或 C,但我已经在所有目标平台上对其进行了测试。 netcat 的问题在于周围有很多不同的版本。其中一些不允许 -e、-c 或 -q 选项。

标签: bash webserver netcat


【解决方案1】:

试试这个:

while true ; do nc -l -p 1500 -c 'echo -e "HTTP/1.1 200 OK\n\n $(date)"'; done

-c使 netcat 在 shell 中执行给定的命令,所以你可以使用 echo。如果不需要回显,请使用-e。有关这方面的更多信息,请尝试man nc。请注意,当使用echo 时,您的程序(date-replacement)无法获取浏览器请求。所以你可能最终想要做这样的事情:

while true ; do nc -l -p 1500 -e /path/to/yourprogram ; done

yourprogram 必须执行协议方面的工作,例如处理 GET、发送 HTTP 200 等。

【讨论】:

  • OpenBSD netcat 工作方式不同,你可以做类似while true; do echo -e "HTTP/1.1 200 OK\n\n $(date)" | nc -l localhost 1500; done
  • 来自man for nc,-p 描述:It is an error to use this option in conjunction with the -l option
  • -l -p 组合来自问题,似乎适用于问题的作者。所以我没有质疑它,而是使用了它。
  • 虽然我看到的 nc 手册页也说使用 -p 选项和 -l 一个是错误的,但官方 netcat 网站在其示例中使用了这两个选项。见:nc110.sourceforge.net
  • nc 至少有 3 种主要“风格”:Hobbit(原版)、BSD / Mac OS XGNU(其中最古老的,以及不再维护)。还有 Nmap 的Ncat。我提出这个问题是因为我认为值得注意的是 -l-p 标志一起仅被视为 nc 的 BSD 风格 中的错误,因此@matlehmann 给出了替代语法跨度>
【解决方案2】:

我遇到了想要返回执行 bash 命令的结果的问题:

$ while true; do { echo -e 'HTTP/1.1 200 OK\r\n'; sh test; } | nc -l 8080; done

注意:
此命令来自:http://www.razvantudorica.com/08/web-server-in-one-line-of-bash

这会执行一个 bash 脚本并将结果返回给连接到在端口 8080 上运行此命令的服务器的浏览器客户端。

我的脚本是这样做的:

$ nano test

#!/bin/bash

echo "************PRINT SOME TEXT***************\n"
echo "Hello World!!!"
echo "\n"

echo "Resources:"
vmstat -S M
echo "\n"

echo "Addresses:"
echo "$(ifconfig)"
echo "\n"


echo "$(gpio readall)"

我的网络浏览器正在显示

************PRINT SOME TEXT***************

Hello World!!!


Resources:
procs -----------memory---------- ---swap-- -----io---- -system-- ----cpu----
 r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa
 0  0      0    314     18     78    0    0     2     1  306   31  0  0 100  0


Addresses:
eth0      Link encap:Ethernet  HWaddr b8:27:eb:86:e8:c5  
          inet addr:192.168.1.83  Bcast:192.168.1.255  Mask:255.255.255.0
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:27734 errors:0 dropped:0 overruns:0 frame:0
          TX packets:26393 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000 
          RX bytes:1924720 (1.8 MiB)  TX bytes:3841998 (3.6 MiB)

lo        Link encap:Local Loopback  
          inet addr:127.0.0.1  Mask:255.0.0.0
          UP LOOPBACK RUNNING  MTU:65536  Metric:1
          RX packets:0 errors:0 dropped:0 overruns:0 frame:0
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0 
          RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)


GPIOs:
+----------+-Rev2-+------+--------+------+-------+
| wiringPi | GPIO | Phys | Name   | Mode | Value |
+----------+------+------+--------+------+-------+
|      0   |  17  |  11  | GPIO 0 | IN   | Low   |
|      1   |  18  |  12  | GPIO 1 | IN   | Low   |
|      2   |  27  |  13  | GPIO 2 | IN   | Low   |
|      3   |  22  |  15  | GPIO 3 | IN   | Low   |
|      4   |  23  |  16  | GPIO 4 | IN   | Low   |
|      5   |  24  |  18  | GPIO 5 | IN   | Low   |
|      6   |  25  |  22  | GPIO 6 | IN   | Low   |
|      7   |   4  |   7  | GPIO 7 | IN   | Low   |
|      8   |   2  |   3  | SDA    | IN   | High  |
|      9   |   3  |   5  | SCL    | IN   | High  |
|     10   |   8  |  24  | CE0    | IN   | Low   |
|     11   |   7  |  26  | CE1    | IN   | Low   |
|     12   |  10  |  19  | MOSI   | IN   | Low   |
|     13   |   9  |  21  | MISO   | IN   | Low   |
|     14   |  11  |  23  | SCLK   | IN   | Low   |
|     15   |  14  |   8  | TxD    | ALT0 | High  |
|     16   |  15  |  10  | RxD    | ALT0 | High  |
|     17   |  28  |   3  | GPIO 8 | ALT2 | Low   |
|     18   |  29  |   4  | GPIO 9 | ALT2 | Low   |
|     19   |  30  |   5  | GPIO10 | ALT2 | Low   |
|     20   |  31  |   6  | GPIO11 | ALT2 | Low   |
+----------+------+------+--------+------+-------+

【讨论】:

  • 最好使用 http 1.0,因为 http 1.1 只是添加连接:keep-alive 在这种情况下很糟糕。
  • @ShimonDoodkin 那毫无意义,因为 netcat 不理解 HTTP。您可以通过传递-k 标志在netcat 中激活keep-alive,而不是通过将Connection: keep-alive 添加到请求标头。
  • 也适用于 Mac 上奇怪的 nc 版本 :)
【解决方案3】:

-q 1 添加到netcat 命令行:

while true; do 
  echo -e "HTTP/1.1 200 OK\n\n $(date)" | nc -l -p 1500 -q 1
done

【讨论】:

  • -q 1 在CentOS 6.x 中不起作用应该用于CentOS 6.x 的地方它说:nc: invalid option -- 'q'while true; do tail -f /usr/local/freeswitch/log/freeswitch.log | nc -l 9999; done &
  • @YumYumYum 我在 OS X 上,没有 freeswitch。 /usr/local/freeswitch/log/freeswitch.log的内容是什么?
  • 添加 -q 1 在 Ubuntu 18.04 上为我工作。希望对您有所帮助。
【解决方案4】:

您面临的问题是 nc 不知道 Web 客户端何时完成其请求,因此它可以响应请求。
网络会话应该是这样的。

TCP session is established.
Browser Request Header: GET / HTTP/1.1
Browser Request Header: Host: www.google.com
Browser Request Header: \n #Note: Browser is telling Webserver that the request header is complete.
Server Response Header: HTTP/1.1 200 OK
Server Response Header: Content-Type: text/html
Server Response Header: Content-Length: 24
Server Response Header: \n #Note: Webserver is telling browser that response header is complete 
Server Message Body: <html>sample html</html>
Server Message Body: \n #Note: Webserver is telling the browser that the requested resource is finished. 
The server closes the TCP session.

以“\n”开头的行只是没有空格的空行,并且只包含一个换行符。

我的 bash httpd 由 xinetd xinetd tutorial 启动。它还将日期、时间、浏览器 IP 地址和整个浏览器请求记录到日志文件中,并计算服务器标头响应的 Content-Length。

user@machine:/usr/local/bin# cat ./bash_httpd
#!/bin/bash
x=0;
Log=$( echo -n "["$(date "+%F %T %Z")"] $REMOTE_HOST ")$(
        while read I[$x] && [ ${#I[$x]} -gt 1 ];do
              echo -n '"'${I[$x]} | sed -e's,.$,",'; let "x = $x + 1";
        done ;
); echo $Log >> /var/log/bash_httpd

Message_Body=$(echo -en '<html>Sample html</html>')
echo -en "HTTP/1.0 200 OK\nContent-Type: text/html\nContent-Length: ${#Message_Body}\n\n$Message_Body"

要添加更多功能,您可以合并。

            METHOD=$(echo ${I[0]} |cut -d" " -f1)
            REQUEST=$(echo ${I[0]} |cut -d" " -f2)
            HTTP_VERSION=$(echo ${I[0]} |cut -d" " -f3)
            If METHOD = "GET" ]; then 
                case "$REQUEST" in

                    "/") Message_Body="HTML formatted home page stuff"
                        ;;
                    /who) Message_Body="HTML formatted results of who"
                        ;;
                    /ps) Message_Body="HTML formatted results of ps"
                        ;;
                    *) Message_Body= "Error Page not found header and content"
                       ;;
                esac

            fi

祝你好运!

【讨论】:

    【解决方案5】:

    另一种方法

    while true; do (echo -e 'HTTP/1.1 200 OK\r\n'; echo -e "\n\tMy website has date function" ; echo -e "\t$(date)\n") | nc -lp 8080; done
    

    让我们使用 curl 使用 2 个 HTTP 请求对其进行测试

    在本例中,172.16.2.6 是服务器 IP 地址。

    服务器端

    admin@server:~$ while true; do (echo -e 'HTTP/1.1 200 OK\r\n'; echo -e "\n\tMy website has date function" ; echo -e "\t$(date)\n") | nc -lp 8080; done
    
    GET / HTTP/1.1 Host: 172.16.2.6:8080 User-Agent: curl/7.48.0 Accept:
    */*
    
    GET / HTTP/1.1 Host: 172.16.2.6:8080 User-Agent: curl/7.48.0 Accept:
    */*
    

    客户端

    user@client:~$ curl 172.16.2.6:8080
    
            My website has date function
            Tue Jun 13 18:00:19 UTC 2017
    
    user@client:~$ curl 172.16.2.6:8080
    
            My website has date function
            Tue Jun 13 18:00:24 UTC 2017
    
    user@client:~$
    

    如果要执行其他命令,请随意替换 $(date)。

    【讨论】:

      【解决方案6】:

      我有同样的需求/问题,但这里没有什么对我有用(或者我不明白所有内容),所以这是我的解决方案。

      我发布了我的 minimum_http_server.sh(使用我的 /bin/bash (4.3.11) 但不是 /bin/sh 因为重定向):

      rm -f out
      mkfifo out
      trap "rm -f out" EXIT
      while true
      do
        cat out | nc -l 1500 > >( # parse the netcat output, to build the answer redirected to the pipe "out".
          export REQUEST=
          while read -r 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 [ -z "$line" ] # empty line / end of request
            then
              # call a script here
              # Note: REQUEST is exported, so the script can parse it (to answer 200/403/404 status code + content)
              ./a_script.sh > out
            fi
          done
        )
      done
      

      还有我的 a_script.sh(根据您的需要):

      #!/bin/bash
      
      echo -e "HTTP/1.1 200 OK\r"
      echo "Content-type: text/html"
      echo
      
      date
      

      【讨论】:

      • 运行 minimum_http_server.sh 后,当我们在 1 秒内多次查询 url 时,它显示页面未找到。但是当我们为每个请求提供 1 秒的间隔时,它工作正常。还注意到,当我们保持此服务运行并从另一个 shell 脚本发出 curl 请求时,该服务会关闭或崩溃。知道可能出了什么问题
      • @satishjohn 从我目前的技能(比当时更好)来看,我刚刚纠正了两个主要缺陷(readtr)和一个小缺陷([ 跟随 elif)。我不会重现你的问题。我不明白为什么 minimum_http_server.sh 会导致这些 1 秒的间隔。设置REQUEST环境变量后,您可以通过多次运行(以与您的curl请求相同的速率)./a_script.sh来确定是minimal_http_server.sh还是您的“a_script.sh”出现故障。
      【解决方案7】:
      mkfifo pipe;
      while true ; 
      do 
         #use read line from pipe to make it blocks before request comes in,
         #this is the key.
         { read line<pipe;echo -e "HTTP/1.1 200 OK\r\n";echo $(date);
         }  | nc -l -q 0 -p 8080 > pipe;  
      
      done
      

      【讨论】:

      • 它是如何工作的? read line&lt;pipe 会等到nc 写入pipe?但是在这一点上,我认为请求在响应写入之前就完成了..?
      • mkfifo 创建一个阻塞管道,这意味着在 nc 读取请求之前不会执行 echo 命令。
      【解决方案8】:

      这是a little bash webserver 的一个美丽之处,我在网上找到了它并复制了一份并稍微修饰了一下——它使用socatnetcat 我已经用socat 对其进行了测试——它是自我包含在一个脚本中并生成自己的配置文件和网站图标。

      默认情况下,它将作为启用 Web 的文件浏览器启动,但可以通过配置文件轻松配置任何逻辑。对于文件,它可以流式传输图像和音乐(mp3)、视频(mp4、avi 等)——我已经测试了将各种文件类型流式传输到 Linux、Windows 和 Android 设备,包括智能手表!

      我认为它实际上比 VLC 更好。我发现它对于将文件传输到无法通过 Web 浏览器访问的远程客户端非常有用,例如Android 智能手表,无需担心物理连接到 USB 端口。

      如果您想尝试一下,只需将其复制并粘贴到名为 bashttpd 的文件中,然后在主机上使用$&gt; bashttpd -s 启动它

      然后您可以转到任何其他计算机(假设防火墙没有阻止到端口 8080 的入站 tcp 连接——默认端口,您可以使用脚本顶部的全局变量将端口更改为您想要的任何端口) . http://bashttpd_server_ip:8080

      #!/usr/bin/env bash
      
      #############################################################################
      ###########################################################################
      ###                          bashttpd v 1.12
      ###
      ### Original author: Avleen Vig,       2012
      ### Reworked by:     Josh Cartwright,  2012
      ### Modified by:     A.M.Danischewski, 2015 
      ### Issues: If you find any issues leave me a comment at 
      ### http://scriptsandoneliners.blogspot.com/2015/04/bashttpd-self-contained-bash-webserver.html 
      ### 
      ### This is a simple Bash based webserver. By default it will browse files and allows for 
      ### retrieving binary files. 
      ### 
      ### It has been tested successfully to view and stream files including images, mp3s, 
      ### mp4s and downloading files of any type including binary and compressed files via  
      ### any web browser. 
      ### 
      ### Successfully tested on various browsers on Windows, Linux and Android devices (including the 
      ### Android Smartwatch ZGPAX S8).  
      ### 
      ### It handles favicon requests by hardcoded favicon image -- by default a marathon 
      ### runner; change it to whatever you want! By base64 encoding your favorit favicon 
      ### and changing the global variable below this header.  
      ### 
      ### Make sure if you have a firewall it allows connections to the port you plan to 
      ### listen on (8080 by default).  
      ### 
      ### By default this program will allow for the browsing of files from the 
      ### computer where it is run.  
      ###  
      ### Make sure you are allowed connections to the port you plan to listen on 
      ### (8080 by default). Then just drop it on a host machine (that has bash) 
      ### and start it up like this:
      ###      
      ### $192.168.1.101> bashttpd -s
      ###      
      ### On the remote machine you should be able to browse and download files from the host 
      ### server via any web browser by visiting:
      ###      
      ### http://192.168.1.101:8080 
      ###  
      #### This program requires (to work to full capacity) by default: 
      ### socat or netcat (w/ '-e' option - on Ubuntu netcat-traditional)
      ### tree - useful for pretty directory listings 
      ### If you are using socat, you can type: bashttpd -s  
      ### 
      ### to start listening on the LISTEN_PORT (default is 8080), you can change 
      ### the port below.  
      ###  E.g.    nc -lp 8080 -e ./bashttpd ## <-- If your nc has the -e option.   
      ###  E.g.    nc.traditional -lp 8080 -e ./bashttpd 
      ###  E.g.    bashttpd -s  -or- socat TCP4-LISTEN:8080,fork EXEC:bashttpd
      ### 
      ### Copyright (C) 2012, Avleen Vig <avleen@gmail.com>
      ### 
      ### Permission is hereby granted, free of charge, to any person obtaining a copy of
      ### this software and associated documentation files (the "Software"), to deal in
      ### the Software without restriction, including without limitation the rights to
      ### use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
      ### the Software, and to permit persons to whom the Software is furnished to do so,
      ### subject to the following conditions:
      ### 
      ### The above copyright notice and this permission notice shall be included in all
      ### copies or substantial portions of the Software.
      ### 
      ### THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
      ### IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
      ### FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
      ### COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
      ### IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
      ### CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
      ### 
      ###########################################################################
      #############################################################################
      
        ### CHANGE THIS TO WHERE YOU WANT THE CONFIGURATION FILE TO RESIDE 
      declare -r BASHTTPD_CONF="/tmp/bashttpd.conf"
      
        ### CHANGE THIS IF YOU WOULD LIKE TO LISTEN ON A DIFFERENT PORT 
      declare -i LISTEN_PORT=8080  
      
       ## If you are on AIX, IRIX, Solaris, or a hardened system redirecting 
       ## to /dev/random will probably break, you can change it to /dev/null.  
      declare -a DUMP_DEV="/dev/random" 
      
       ## Just base64 encode your favorite favicon and change this to whatever you want.    
      declare -r FAVICON="AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAADg4+3/srjc/5KV2P+ortn/xMrj/6Ch1P+Vl9f/jIzc/3572f+CgNr/fnzP/3l01f+Ih9r/h4TZ/8fN4//P1Oj/3uPr/7O+1v+xu9X/u8XY/9bi6v+UmdD/XV26/3F1x/+GitT/VVXC/3x/x/+HjNT/lp3Z/6633f/E0eD/2ePr/+bt8v/U4+v/0uLp/9Xj6//Z5e3/oKbX/0pJt/9maML/cHLF/3p8x//T3+n/3Ofu/9vo7//W5Oz/0uHq/9zn7f/j6vD/1OLs/8/f6P/R4Oj/1OPr/7jA4f9KSbf/Skm3/3p/yf/U4ez/1ePq/9rn7//Z5e3/0uHp/87e5//a5Ov/5Ovw/9Hf6v/T4uv/1OLp/9bj6/+kq9r/Skq3/0pJt/+cotb/zdnp/9jl7f/a5u//1+Ts/9Pi6v/O3ub/2uXr/+bt8P/Q3un/0eDq/9bj7P/Z5u7/r7jd/0tKt/9NTLf/S0u2/8zW6v/c5+//2+fv/9bj6//S4un/zt3m/9zm7P/k7PD/1OPr/9Li7P/V5Oz/2OXt/9jl7v+HjM3/lZvT/0tKt/+6w+L/2ebu/9fk7P/V4+v/0uHq/83d5v/a5ev/5ezw/9Pi6v/U4+z/1eXs/9bj6//b5+//vsjj/1hYvP9JSLb/horM/9nk7P/X5e3/1eTs/9Pi6v/P3uf/2eXr/+Tr7//O3+n/0uLr/9Xk7P/Y5e3/w8/k/7XA3/9JR7f/SEe3/2lrw//G0OX/1uLr/9Xi7P/T4ev/0N/o/9zn7f/k7PD/zN3p/8rd5v/T4ur/1ePt/5We0/+0w9//SEe3/0pKt/9OTrf/p7HZ/7fD3//T4uv/0N/o/9Hg6f/d5+3/5ezw/9Li6//T4uv/2ubu/8PQ5f9+hsr/ucff/4eOzv+Ei8z/rLja/8zc6P/I1+b/0OLq/8/f6P/Q4Oj/3eft/+bs8f/R4On/0+Lq/9Tj6v/T4Ov/wM7h/9Df6f/M2uf/z97q/9Dg6f/Q4On/1OPr/9Tj6//S4ur/0ODp/93o7f/n7vH/0N/o/8/f5//P3+b/2OXt/9zo8P/c6fH/zdjn/7fB3/+3weD/1eLs/9nn7//V5Oz/0+Lr/9Pi6//e6O7/5u3x/9Pi6v/S4en/0uLp/9Tj6//W4+v/3Ojw/9rm7v9vccT/wcvm/9rn7//X5Oz/0uHq/9Hg6f/S4er/3uju/+bt8f/R4On/0uHp/9Xk6//Y5u7/1OTs/9bk7P/W5Ov/XFy9/2lrwf/a5+//1uPr/9Pi6v/U4er/0eHq/93o7v/v8vT/5ezw/+bt8f/o7vL/6e/z/+jv8v/p7/L/6e/y/9XZ6//IzOX/6e7y/+nv8v/o7vL/5+7x/+ft8f/r8PP/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==" 
      
      declare -i DEBUG=1 
      declare -i VERBOSE=0
      declare -a REQUEST_HEADERS
      declare    REQUEST_URI="" 
      declare -a HTTP_RESPONSE=(
         [200]="OK"
         [400]="Bad Request"
         [403]="Forbidden"
         [404]="Not Found"
         [405]="Method Not Allowed"
         [500]="Internal Server Error")
      declare DATE=$(date +"%a, %d %b %Y %H:%M:%S %Z")
      declare -a RESPONSE_HEADERS=(
            "Date: $DATE"
         "Expires: $DATE"
          "Server: Slash Bin Slash Bash"
      )
      
      function warn() { ((${VERBOSE})) && echo "WARNING: $@" >&2; }
      
      function chk_conf_file() { 
      [ -r "${BASHTTPD_CONF}" ] || {
         cat >"${BASHTTPD_CONF}" <<'EOF'
      #
      # bashttpd.conf - configuration for bashttpd
      #
      # The behavior of bashttpd is dictated by the evaluation
      # of rules specified in this configuration file.  Each rule
      # is evaluated until one is matched.  If no rule is matched,
      # bashttpd will serve a 500 Internal Server Error.
      #
      # The format of the rules are:
      #    on_uri_match REGEX command [args]
      #    unconditionally command [args]
      #
      # on_uri_match:
      #   On an incoming request, the URI is checked against the specified
      #   (bash-supported extended) regular expression, and if encounters a match the
      #   specified command is executed with the specified arguments.
      #
      #   For additional flexibility, on_uri_match will also pass the results of the
      #   regular expression match, ${BASH_REMATCH[@]} as additional arguments to the
      #   command.
      #
      # unconditionally:
      #   Always serve via the specified command.  Useful for catchall rules.
      #
      # The following commands are available for use:
      #
      #   serve_file FILE
      #     Statically serves a single file.
      #
      #   serve_dir_with_tree DIRECTORY
      #     Statically serves the specified directory using 'tree'.  It must be
      #     installed and in the PATH.
      #
      #   serve_dir_with_ls DIRECTORY
      #     Statically serves the specified directory using 'ls -al'.
      #
      #   serve_dir  DIRECTORY
      #     Statically serves a single directory listing.  Will use 'tree' if it is
      #     installed and in the PATH, otherwise, 'ls -al'
      #
      #   serve_dir_or_file_from DIRECTORY
      #     Serves either a directory listing (using serve_dir) or a file (using
      #     serve_file).  Constructs local path by appending the specified root
      #     directory, and the URI portion of the client request.
      #
      #   serve_static_string STRING
      #     Serves the specified static string with Content-Type text/plain.
      #
      # Examples of rules:
      #
      # on_uri_match '^/issue$' serve_file "/etc/issue"
      #
      #   When a client's requested URI matches the string '/issue', serve them the
      #   contents of /etc/issue
      #
      # on_uri_match 'root' serve_dir /
      #
      #   When a client's requested URI has the word 'root' in it, serve up
      #   a directory listing of /
      #
      # DOCROOT=/var/www/html
      # on_uri_match '/(.*)' serve_dir_or_file_from "$DOCROOT"
      #   When any URI request is made, attempt to serve a directory listing
      #   or file content based on the request URI, by mapping URI's to local
      #   paths relative to the specified "$DOCROOT"
      #
      #unconditionally serve_static_string 'Hello, world!  You can configure bashttpd by modifying bashttpd.conf.'
      DOCROOT=/
      on_uri_match '/(.*)' serve_dir_or_file_from 
      # More about commands:
      #
      # It is possible to somewhat easily write your own commands.  An example
      # may help.  The following example will serve "Hello, $x!" whenever
      # a client sends a request with the URI /say_hello_to/$x:
      #
      # serve_hello() {
      #    add_response_header "Content-Type" "text/plain"
      #    send_response_ok_exit <<< "Hello, $2!"
      # }
      # on_uri_match '^/say_hello_to/(.*)$' serve_hello
      #
      # Like mentioned before, the contents of ${BASH_REMATCH[@]} are passed
      # to your command, so its possible to use regular expression groups
      # to pull out info.
      #
      # With this example, when the requested URI is /say_hello_to/Josh, serve_hello
      # is invoked with the arguments '/say_hello_to/Josh' 'Josh',
      # (${BASH_REMATCH[0]} is always the full match)
      EOF
         warn "Created bashttpd.conf using defaults.  Please review and configure bashttpd.conf before running bashttpd again."
      #  exit 1
      } 
      }
      
      function recv() { ((${VERBOSE})) && echo "< $@" >&2; }
      
      function send() { ((${VERBOSE})) && echo "> $@" >&2; echo "$*"; }
      
      function add_response_header() { RESPONSE_HEADERS+=("$1: $2"); }
      
      function send_response_binary() {
        local code="$1"
        local file="${2}" 
        local transfer_stats="" 
        local tmp_stat_file="/tmp/_send_response_$$_"
        send "HTTP/1.0 $1 ${HTTP_RESPONSE[$1]}"
        for i in "${RESPONSE_HEADERS[@]}"; do
           send "$i"
        done
        send
       if ((${VERBOSE})); then 
         ## Use dd since it handles null bytes
        dd 2>"${tmp_stat_file}" < "${file}" 
        transfer_stats=$(<"${tmp_stat_file}") 
        echo -en ">> Transferred: ${file}\n>> $(awk '/copied/{print}' <<< "${transfer_stats}")\n" >&2  
        rm "${tmp_stat_file}"
       else 
         ## Use dd since it handles null bytes
        dd 2>"${DUMP_DEV}" < "${file}"   
       fi 
      }   
      
      function send_response() {
        local code="$1"
        send "HTTP/1.0 $1 ${HTTP_RESPONSE[$1]}"
        for i in "${RESPONSE_HEADERS[@]}"; do
           send "$i"
        done
        send
        while IFS= read -r line; do
           send "${line}"
        done
      }
      
      function send_response_ok_exit() { send_response 200; exit 0; }
      
      function send_response_ok_exit_binary() { send_response_binary 200  "${1}"; exit 0; }
      
      function fail_with() { send_response "$1" <<< "$1 ${HTTP_RESPONSE[$1]}"; exit 1; }
      
      function serve_file() {
        local file="$1"
        local CONTENT_TYPE=""
        case "${file}" in
          *\.css)
            CONTENT_TYPE="text/css"
            ;;
          *\.js)
            CONTENT_TYPE="text/javascript"
            ;;
          *)
            CONTENT_TYPE=$(file -b --mime-type "${file}")
            ;;
        esac
        add_response_header "Content-Type"  "${CONTENT_TYPE}"
        CONTENT_LENGTH=$(stat -c'%s' "${file}") 
        add_response_header "Content-Length" "${CONTENT_LENGTH}"
          ## Use binary safe transfer method since text doesn't break. 
        send_response_ok_exit_binary "${file}"
      }
      
      function serve_dir_with_tree() {
        local dir="$1" tree_vers tree_opts basehref x
          ## HTML 5 compatible way to avoid tree html from generating favicon
          ## requests in certain browsers, such as browsers in android smartwatches. =) 
        local no_favicon=" <link href=\"data:image/x-icon;base64,${FAVICON}\" rel=\"icon\" type=\"image/x-icon\" />"  
        local tree_page="" 
        local base_server_path="/${2%/}"
        [ "$base_server_path" = "/" ] && base_server_path=".." 
        local tree_opts="--du -h -a --dirsfirst" 
        add_response_header "Content-Type" "text/html"
         # The --du option was added in 1.6.0.   "/${2%/*}"
        read _ tree_vers x < <(tree --version)
        tree_page=$(tree -H "$base_server_path" -L 1 "${tree_opts}" -D "${dir}")
        tree_page=$(sed "5 i ${no_favicon}" <<< "${tree_page}")  
        [[ "${tree_vers}" == v1.6* ]] 
        send_response_ok_exit <<< "${tree_page}"  
      }
      
      function serve_dir_with_ls() {
        local dir="$1"
        add_response_header "Content-Type" "text/plain"
        send_response_ok_exit < \
           <(ls -la "${dir}")
      }
      
      function serve_dir() {
        local dir="$1"
         # If `tree` is installed, use that for pretty output.
        which tree &>"${DUMP_DEV}" && \
           serve_dir_with_tree "$@"
        serve_dir_with_ls "$@"
        fail_with 500
      }
      
      function urldecode() { [ "${1%/}" = "" ] && echo "/" ||  echo -e "$(sed 's/%\([[:xdigit:]]\{2\}\)/\\\x\1/g' <<< "${1%/}")"; } 
      
      function serve_dir_or_file_from() {
        local URL_PATH="${1}/${3}"
        shift
        URL_PATH=$(urldecode "${URL_PATH}") 
        [[ $URL_PATH == *..* ]] && fail_with 400
         # Serve index file if exists in requested directory
        [[ -d "${URL_PATH}" && -f "${URL_PATH}/index.html" && -r "${URL_PATH}/index.html" ]] && \
           URL_PATH="${URL_PATH}/index.html"
        if [[ -f "${URL_PATH}" ]]; then
           [[ -r "${URL_PATH}" ]] && \
              serve_file "${URL_PATH}" "$@" || fail_with 403
        elif [[ -d "${URL_PATH}" ]]; then
           [[ -x "${URL_PATH}" ]] && \
              serve_dir  "${URL_PATH}" "$@" || fail_with 403
        fi
        fail_with 404
      }
      
      function serve_static_string() {
        add_response_header "Content-Type" "text/plain"
        send_response_ok_exit <<< "$1"
      }
      
      function on_uri_match() {
        local regex="$1"
        shift
        [[ "${REQUEST_URI}" =~ $regex ]] && \
           "$@" "${BASH_REMATCH[@]}"
      }
      
      function unconditionally() { "$@" "$REQUEST_URI"; }
      
      function main() { 
        local recv="" 
        local line="" 
        local REQUEST_METHOD=""
        local REQUEST_HTTP_VERSION="" 
        chk_conf_file
        [[ ${UID} = 0 ]] && warn "It is not recommended to run bashttpd as root."
         # Request-Line HTTP RFC 2616 $5.1
        read -r line || fail_with 400
        line=${line%%$'\r'}
        recv "${line}"
        read -r REQUEST_METHOD REQUEST_URI REQUEST_HTTP_VERSION <<< "${line}"
        [ -n "${REQUEST_METHOD}" ] && [ -n "${REQUEST_URI}" ] && \
         [ -n "${REQUEST_HTTP_VERSION}" ] || fail_with 400
         # Only GET is supported at this time
        [ "${REQUEST_METHOD}" = "GET" ] || fail_with 405
        while IFS= read -r line; do
          line=${line%%$'\r'}
          recv "${line}"
            # If we've reached the end of the headers, break.
          [ -z "${line}" ] && break
          REQUEST_HEADERS+=("${line}")
        done
      } 
      
      if [[ ! -z "{$1}" ]] && [ "${1}" = "-s" ]; then 
       socat TCP4-LISTEN:${LISTEN_PORT},fork EXEC:"${0}" 
      else 
       main 
       source "${BASHTTPD_CONF}" 
       fail_with 500
      fi 
      

      【讨论】:

      • 如何修改它以接受 POST 和 GET?
      • @Adam Dymitruk 您必须添加逻辑来处理 Post 请求(不是那么难),这里是最新源代码的链接,查看 main 函数,其中显示 [ "${REQUEST_METHOD}" = "GET" ] || fail_with 405github.com/AdamDanischewski/bashttpd/blob/master/bashttpd
      【解决方案9】:

      LOL,一个超级蹩脚的黑客,但至少 curl 和 firefox 接受它:

      while true ; do (dd if=/dev/zero count=10000;echo -e "HTTP/1.1\n\n $(date)") | nc -l  1500  ; done
      

      你最好尽快用合适的东西替换它!

      啊,是的,我的nc 和你的不完全一样,它不喜欢-p 选项。

      【讨论】:

      • 此答案适用于 OS X 10.10.1 上的 netcat。超级棒!
      【解决方案10】:

      如果您使用的是 Apline Linux,BusyBox netcat 会略有不同:

      while true; do nc -l -p 8080 -e sh -c 'echo -e "HTTP/1.1 200 OK\n\n$(date)"'; done
      

      还有另一种使用printf的方式:

      while true; do nc -l -p 8080 -e sh -c "printf 'HTTP/1.1 200 OK\n\n%s' \"$(date)\""; done
      

      【讨论】:

      • echo -e 不可靠,尤其是sh。严格符合 POSIX 标准的 echo 会将 -e 视为写入标准输出的数据,而不是修改其行为的标志;改用printf 以获得可靠的结果:例如printf '%s\n' 'HTTP/1.1 200 OK' '' "$(date)"
      【解决方案11】:
      while true; do (echo -e 'HTTP/1.1 200 OK\r\nConnection: close\r\n';) | timeout 1  nc -lp 8080 ; done
      

      1 秒后关闭连接,因此 curl 不会挂起。

      【讨论】:

      • 我的 bash shell 在我尝试后挂了。 Ctrl+Z 没有终止进程。我不得不生成另一个 shell 实例并删除前一个。
      【解决方案12】:

      输入 nc -h 并查看您是否有 -e 选项可用。如果是,您可以创建一个脚本,例如:

      script.sh

      echo -e "HTTP/1.1 200 OK\n\n $(date)"
      

      然后像这样运行它:

      while true ; do nc -l -p 1500 -e script.sh; done
      

      请注意,-e 选项需要在编译时启用。

      【讨论】:

        【解决方案13】:

        我认为列出的所有解决方案都不起作用的问题是http服务的本质所固有的,建立的每个请求都针对不同的客户端,并且响应需要在不同的上下文中处理,每个请求都必须fork 一个新的响应实例...

        我认为目前的解决方案是netcat-e 但我不知道为什么不起作用...也许是我在openwrt 上测试的nc 版本...

        socat 可以工作....

        我试试这个https://github.com/avleen/bashttpd

        它可以工作,但我必须使用这个命令运行 shell 脚本。

        socat tcp-l:80,reuseaddr,fork EXEC:bashttpd &
        

        github 上的 socatnetcat 示例不适用于我,但我使用的 socat 有效。

        【讨论】:

        • reuseaddr 标记你让我开心!谢谢你
        【解决方案14】:

        实际上,优雅地关闭连接的最佳方式是发送Content-Length 标头,如下所示。客户端(如curl收到数据后会关闭连接。

        DATA="Date: $(date)"; 
        LENGTH=$(echo $DATA | wc -c);
        echo -e "HTTP/1.1 200 OK\nContent-Length: ${LENGTH}\n\n${DATA}" | nc -l -p 8000;
        

        【讨论】:

        • echo $DATA | wc -c 的长度不一定与"$DATA" 相同:当您将其放在 echo 中的引号之外时,您会扩展通配符,将空格的运行更改为单个空格等。使用length=${#data} 代替(根据POSIX-specified convention 使用小写变量名;阅读该文档,记住环境变量和常规 shell 变量共享一个命名空间)。
        【解决方案15】:

        在 OSX 上你可以使用:

        while true; do echo -e "HTTP/1.1 200 OK\n\n $(date)" | nc -l  localhost 1500 ; done
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2017-08-05
          • 2017-03-04
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-05-02
          相关资源
          最近更新 更多