Web服务器shell脚本是否可以使用标准输入/输出来处理多个请求?

时间:2016-11-25 10:57:53

标签: shell http

我想扩展this example webserver 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 OK\r"
  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 Found\r"
  echo -e "Content-Type: text/html\r"
  echo -e "\r"
  echo -e "404 Not Found\r"
  echo -e "Not Found
           The requested resource was not found\r"
  echo -e "\r"
fi

done

将代码包装在循环中是不够的,因为浏览器不会渲染任何内容。我怎样才能做到这一点?

特定于应用程序的原因使得启动脚本 per-request 成为一种不合适的方法。

TCP侦听器需要接受浏览器连接并将它们连接到脚本。我使用socat来执行此操作:

$ socat EXEC:./webserver TCP4-LISTEN:8080,reuseaddr,fork

通过将浏览器指向http://localhost:8080

,可以访问服务器

2 个答案:

答案 0 :(得分:0)

浏览器需要知道预期的数据量,并且它不会渲染任何内容 它获取该数据或连接由服务器关闭。

HTTP响应应该包含Content-Length标头,或者它应该使用* chunked * Transfer-Encoding

example script不会这样做。但是,它可以工作,因为它处理单个请求  和退出导致连接关闭。

因此,解决问题的一种方法是设置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 OK\r"
  echo -e "Content-Type: text/html\r"
  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 Found\r"
  echo -e "Content-Type: text/html\r"
  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

答案 1 :(得分:0)

另一个解决方案是让脚本触发连接关闭。一种方法是发送socat可以解释为EOF的转义码。

例如,在回复的末尾添加BELL character code(ASCII 7,\a):

echo -e '\a'

并告诉socat将其解释为EOF:

$ socat EXEC:./webserver,escape=7 TCP4-LISTEN:8080,reuseaddr,fork

任何通常未使用的角色都可以,BELL就是一个例子。

虽然上述方法可行,但HTTP实际上应该包含内容类型或传输编码标头。如果使用类似的技术从脚本提供任意(非HTTP)请求,则此替代方法可能很有用。