Web服务器shell脚本可以使用标准输入/输出来提供多个请求吗?

时间:2023-01-25 21:00:18

I want to extend this example webserver shell script to handle multiple requests. Here is the example source:

我想扩展这个示例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

Wrapping the code in a loop is insufficient because the browser doesn't render anything. How can I make this work ?

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

Application-specific reasons make launching the script per-request an unsuitable approach.

特定于应用程序的原因使得按请求启动脚本是一种不合适的方法。


A TCP listener is required to accept browser connections and connect them to the script. I used socat to do this:

TCP侦听器需要接受浏览器连接并将它们连接到脚本。我使用socat来做到这一点:

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

This gives access to the server by pointing a browser at http://localhost:8080.

这通过将浏览器指向http:// localhost:8080来访问服务器。

2 个解决方案

#1


0  

The browser needs to know how much data to expect, and it won't render anything until it gets that data or the connection is closed by the server.

浏览器需要知道预期的数据量,并且在获取数据或服务器关闭连接之前,它不会呈现任何内容。

The HTTP response should include a Content-Length header or it should use a *chunked * Transfer-Encoding.

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

The example script does not do that. However, it works because it processes a single request and exits which causes the connection to close.

示例脚本不会这样做。但是,它可以工作,因为它处理单个请求并退出,导致连接关闭。

So, one way to solve the problem is to set a Content-Length header. Here is an example that works:

因此,解决问题的一种方法是设置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

#2


0  

Another solution is to make the script trigger the connection close. One way to do this is to send an escape code that socat can interpret as EOF.

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

For example, add a BELL character code (ASCII 7, \a) to the end of the response:

例如,在响应的末尾添加BELL字符代码(ASCII 7,\ a):

echo -e '\a'

and tell socat to interpret it as EOF:

并告诉socat将其解释为EOF:

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

Any usually unused character will do, BELL is just an example.

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

Although the above will work, HTTP should really contain a content type or transfer encoding header. This alternative method may be useful if using a similar technique to serve arbitrary (non-HTTP) requests from a script.

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

#1


0  

The browser needs to know how much data to expect, and it won't render anything until it gets that data or the connection is closed by the server.

浏览器需要知道预期的数据量,并且在获取数据或服务器关闭连接之前,它不会呈现任何内容。

The HTTP response should include a Content-Length header or it should use a *chunked * Transfer-Encoding.

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

The example script does not do that. However, it works because it processes a single request and exits which causes the connection to close.

示例脚本不会这样做。但是,它可以工作,因为它处理单个请求并退出,导致连接关闭。

So, one way to solve the problem is to set a Content-Length header. Here is an example that works:

因此,解决问题的一种方法是设置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

#2


0  

Another solution is to make the script trigger the connection close. One way to do this is to send an escape code that socat can interpret as EOF.

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

For example, add a BELL character code (ASCII 7, \a) to the end of the response:

例如,在响应的末尾添加BELL字符代码(ASCII 7,\ a):

echo -e '\a'

and tell socat to interpret it as EOF:

并告诉socat将其解释为EOF:

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

Any usually unused character will do, BELL is just an example.

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

Although the above will work, HTTP should really contain a content type or transfer encoding header. This alternative method may be useful if using a similar technique to serve arbitrary (non-HTTP) requests from a script.

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