我想扩展此示例网络服务器shell脚本以处理多个请求。这是来源:
#!/bin/sh
# based on https://debian-administration.org/article/371/A_web_server_in_a_shell_script
base=/srv/content
while /bin/true
do
read request
while /bin/true; do
read header
[ "$header" == $'r' ] && break;
done
url="${request#GET }"
url="${url% HTTP/*}"
filename="$base$url"
if [ -f "$filename" ]; then
echo -e "HTTP/1.1 200 OKr"
echo -e "Content-Type: `/usr/bin/file -bi "$filename"`r"
echo -e "r"
cat "$filename"
echo -e "r"
else
echo -e "HTTP/1.1 404 Not Foundr"
echo -e "Content-Type: text/htmlr"
echo -e "r"
echo -e "404 Not Foundr"
echo -e "Not Found
The requested resource was not foundr"
echo -e "r"
fi
done
将代码包装在循环中是不够的,因为浏览器不会渲染任何内容。我该如何做这项工作?
特定于应用程序的原因使启动脚本每次请求不合适的方法。
需要TCP侦听器接受浏览器连接并将其连接到脚本。我使用socat
来做到这一点:
$ socat EXEC:./webserver TCP4-LISTEN:8080,reuseaddr,fork
通过将浏览器指向http://localhost:8080
。
浏览器需要知道要期望多少数据,并且不会渲染任何内容它获取该数据或连接由服务器关闭。
HTTP响应应包括Content-Length
标头,否则应使用 *块*转移编码。
示例脚本不这样做。但是,它起作用是因为它处理了一个请求 并导致连接关闭的退出。
因此,解决问题的一种方法是设置Content-Length
标头。这是一个有效的示例:
#!/bin/sh
# stdio webserver based on https://debian-administration.org/article/371/A_web_server_in_a_shell_script
respond_with() {
echo -e "HTTP/1.1 200 OKr"
echo -e "Content-Type: text/htmlr"
echo -e "Content-Length: ${#1}r"
echo -e "r"
echo "<pre>${1}</pre>"
echo -e "r"
}
respond_not_found() {
content='<h1>Not Found</h1>
<p>The requested resource was not found</p>'
echo -e "HTTP/1.1 404 Not Foundr"
echo -e "Content-Type: text/htmlr"
echo -e "Content-Length: ${#content}r"
echo -e "r"
echo "${content}"
echo -e "r"
}
base='/var/www'
while /bin/true; do
read request
while /bin/true; do
read header
[ "$header" == $'r' ] && break;
done
url="${request#GET }"
url="${url% HTTP/*}"
filename="$base/$url"
if [ -f "$filename" ]; then
respond_with "$(cat $filename)"
elif [ -d "$filename" ]; then
respond_with "$(ls -l $filename)"
else
respond_not_found
fi
done
另一个解决方案是使脚本触发连接关闭。一种方法是发送socat
可以解释为EOF的逃生代码。
例如,在响应末尾添加钟字符代码(ASCII 7,a
):
echo -e 'a'
并告诉socat
将其解释为EOF:
$ socat EXEC:./webserver,escape=7 TCP4-LISTEN:8080,reuseaddr,fork
任何通常未使用的角色都会做的,贝尔就是一个例子。
尽管上述功能将起作用,但HTTP应该真正包含内容类型或传输编码标头。如果使用类似技术从脚本提供任意(非HTTP)请求时,此替代方法可能很有用。