bash 使用 netcat 的最小 Web 服务器

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/16640054/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-09 23:44:19  来源:igfitidea点击:

Minimal web server using netcat

bashwebservernetcat

提问by andwagon

I'm trying to set up a minimal web server using netcat (nc). When the browser calls up localhost:1500, for instance, it should show the result of a function (datein the example below, but eventually it'll be a python or c program that yields some data). My little netcat web server needs to be a while true loop in bash, possibly as simple as this:

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

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

When I try this the browser shows the currently available data during the moment when nc starts. I want the browser displays the data during the moment the browser requests it, though. How can I achieve this?

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

回答by Constantin Berhard

Try this:

尝试这个:

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

The -cmakes netcat execute the given command in a shell, so you can use echo. If you don't need echo, use -e. For further information on this, try man nc. Note, that when using echothere is no way for your program (the date-replacement) to get the browser request. So you probably finally want to do something like this:

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

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

Where yourprogrammust do the protocol stuff like handling GET, sending HTTP 200 etc.

yourprogram必须在哪里做协议的事情,比如处理 GET、发送 HTTP 200 等。

回答by maco1717

Donno how or why but i manage to find this around and it works for me, i had the problem I wanted to return the result of executing a bash

不知道如何或为什么,但我设法找到了它并且它对我有用,我遇到了问题,我想返回执行 bash 的结果

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

NOTE:This command was taken from: http://www.razvantudorica.com/08/web-server-in-one-line-of-bash

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

this executes bash script test and return the result to a browser client connecting to the server running this command on port 8080

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

My script does this ATM

我的脚本做这个 ATM

$ 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)"

and my web browser is showing

我的网络浏览器正在显示

************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   |
+----------+------+------+--------+------+-------+

simply amazing!

简直太神奇了!

回答by Ansgar Wiechers

Add -q 1to the netcatcommand line:

添加-q 1netcat命令行:

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

回答by Keith Reynolds

The problem you are facing is that nc does not know when the web client is done with its request so it can respond to the request.
A web session should go something like this.

您面临的问题是 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.

Lines that begin with "\n" are simply empty lines without even a space and contain nothing more than a new line character.

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

I have my bash httpd launched by xinetd, xinetd tutorial. It also logs date, time, browser IP address, and the entire browser request to a log file, and calculates Content-Length for the Server header response.

我的 bash httpd 是由 xinetd 启动的,xinetd 教程。它还将日期、时间、浏览器 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"

To add more functionality, you could incorporate.

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

            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

Happy bashing!

快乐扑街!

回答by Charlotte Russell

Another way to do this

另一种方法来做到这一点

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

Let's test it with 2 HTTP request using curl

让我们使用 curl 用 2 个 HTTP 请求测试它

In this example, 172.16.2.6 is the server IP Address.

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

Server Side

服务器端

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:
*/*

Client Side

客户端

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:~$

If you want to execute another command, feel free to replace $(date).

如果你想执行另一个命令,可以随意替换 $(date)。

回答by syme

I had the same need/problem but nothing here worked for me (or I didn't understand everything), so this is my solution.

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

I post my minimal_http_server.sh (working with my /bin/bash (4.3.11) but not /bin/sh because of the redirection):

我发布了我的 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

And my a_script.sh (with your need):

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

#!/bin/bash

echo -e "HTTP/1.1 200 OK\r"
echo "Content-type: text/html"
echo

date

回答by eeeyes

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

回答by eeeyes

Here is a beauty of a little bash webserver, I found it online and forked a copy and spruced it up a bit - it uses socator netcatI have tested it with socat-- it is self-contained in one-script and generates its own configuration file and favicon.

这是一个小 bash 网络服务器的美妙之处,我在网上找到了它并分叉了一个副本并对其进行了一些修饰 - 它使用socatnetcat我已经测试过它socat- 它独立于一个脚本并生成自己的配置文件和网站图标。

By default it will start up as a web enabled file browser yet is easily configured by the configuration file for any logic. For files it streams images and music (mp3's), video (mp4's, avi, etc) -- I have tested streaming various file types to Linux,Windows and Android devices including a smartwatch!

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

I think it streams better than VLC actually. I have found it useful for transferring files to remote clients who have no access beyond a web browser e.g. Android smartwatch without needing to worry about physically connecting to a USB port.

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

If you want to try it out just copy and paste it to a file named bashttpd, then start it up on the host with $> bashttpd -s

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

Then you can go to any other computer (presuming the firewall is not blocking inbound tcp connections to port 8080 -- the default port, you can change the port to whatever you want using the global variables at the top of the script). http://bashttpd_server_ip:8080

然后您可以转到任何其他计算机(假设防火墙没有阻止到端口 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:
###      
### 2.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 <[email protected]>
### 
### 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, !"
# }
# 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+=(": "); }

function send_response_binary() {
  local code=""
  local file="" 
  local transfer_stats="" 
  local tmp_stat_file="/tmp/_send_response_$$_"
  send "HTTP/1.0  ${HTTP_RESPONSE[]}"
  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=""
  send "HTTP/1.0  ${HTTP_RESPONSE[]}"
  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  ""; exit 0; }

function fail_with() { send_response "" <<< " ${HTTP_RESPONSE[]}"; exit 1; }

function serve_file() {
  local file=""
  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="" 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=""
  add_response_header "Content-Type" "text/plain"
  send_response_ok_exit < \
     <(ls -la "${dir}")
}

function serve_dir() {
  local dir=""
   # 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/g' <<< "${1%/}")"; } 

function serve_dir_or_file_from() {
  local URL_PATH="/"
  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 <<< ""
}

function on_uri_match() {
  local regex=""
  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 .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 "{}" ]] && [ "" = "-s" ]; then 
 socat TCP4-LISTEN:${LISTEN_PORT},fork EXEC:"
while true ; do (dd if=/dev/zero count=10000;echo -e "HTTP/1.1\n\n $(date)") | nc -l  1500  ; done
" else main source "${BASHTTPD_CONF}" fail_with 500 fi

回答by mogul

LOL, a super lame hack, but at least curl and firefox accepts it:

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

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

You better replace it soon with something proper!

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

Ah yes, my ncwere not exactly the same as yours, it did not like the -poption.

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

回答by cstroe

If you're using Apline Linux, the BusyBox netcat is slightly different:

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

##代码##