使用 netcat 的最小 Web 服务器

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

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

当我尝试这一点,浏览器显示当前可用的数据时,数控启动。但是,我希望浏览器在浏览器请求时显示数据。我怎么才能做到呢?

194594 次浏览

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的选项。

-q 1添加到 netcat命令行:

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

键入 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选项才能使用。

您面临的问题是,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”开头的行是简单的空行,甚至没有空格,只包含一个新的行字符。

我已经通过 xinetd,Xinetd 教程启动了 bash httpd。它还将日期、时间、浏览器 IP 地址和整个浏览器请求记录到日志文件中,并计算 Server 头响应的 Content-Llength。

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

打得开心!

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

试试这个:

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

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

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

其中 yourprogram必须做协议的事情,如处理 GET,发送 HTTP200等。

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

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

注意:
这个命令来自: < a href = “ https://web.archive./web/20130817001428/http://www.razvantudorica.com/08/web-server-in-one-line-of-bash/”rel = “ nofollow norefrer”> 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 _ 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

我认为问题是所有列出的解决方案都不能正常工作,这是 http 服务固有的本质,每个建立的请求都是与不同的客户端,响应需要在不同的上下文中处理,每个请求都必须分叉一个新的响应实例..。

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

socat一起工作。

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

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

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

Github 上的 socatnetcat样本对我来说不起作用,但是我使用的 socat起作用了。

这是一个美丽的 一个小型网络服务器,我在网上找到了它,分叉一个副本,并整理了一下-它使用 socatnetcat我已经测试了它与 socat-它是独立的一个脚本,并生成自己的配置文件和图标。

默认情况下,它将作为一个启用 web 的文件浏览器启动,但很容易配置文件的任何逻辑。对于文件,它流图像和音乐(mp3) ,视频(mp4,avi,等等)-我已经测试了流各种类型的文件到 Linux,Windows 和 Android 设备,包括智能手表!

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

如果您想尝试使用它,只需将其复制并粘贴到名为 bashttpd 的文件中,然后使用 $> 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

另一种方法

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)。

如果你使用的是 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

实际上,优雅地关闭连接的最佳方法是像下面这样发送 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;
while true; do (echo -e 'HTTP/1.1 200 OK\r\nConnection: close\r\n';) | timeout 1  nc -lp 8080 ; done

1秒后关闭连接,这样 curl 就不会挂在上面。

在 OSX 上你可以使用:

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